<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on lanefu blog</title><link>https://blog.lane-fu.com/posts/</link><description>Recent content in Posts on lanefu blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Sat, 07 Feb 2026 19:34:38 -0500</lastBuildDate><atom:link href="https://blog.lane-fu.com/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>First Openclaw Attempt</title><link>https://blog.lane-fu.com/posts/2026/02/first-openclaw-attempt/</link><pubDate>Sat, 07 Feb 2026 19:34:38 -0500</pubDate><guid>https://blog.lane-fu.com/posts/2026/02/first-openclaw-attempt/</guid><description>&lt;h2 id="level-setting">level setting&lt;/h2>
&lt;p>This blog post is going to be super sloppy. Human sloppy. I just felt like I
should at least log something.&lt;/p>
&lt;h2 id="okay-fine-i-have-to-try-it">Okay fine, I have to try it&lt;/h2>
&lt;p>All my nerd feeds are talking about it&amp;hellip; It&amp;rsquo;s super intriguing. The youtubers..
Naturally anybody that has a tech job in the US also needs to level up on AI as
much as possible. So okay fine. I have to try it.&lt;/p></description><content type="html"><![CDATA[<h2 id="level-setting">level setting</h2>
<p>This blog post is going to be super sloppy. Human sloppy. I just felt like I
should at least log something.</p>
<h2 id="okay-fine-i-have-to-try-it">Okay fine, I have to try it</h2>
<p>All my nerd feeds are talking about it&hellip; It&rsquo;s super intriguing. The youtubers..
Naturally anybody that has a tech job in the US also needs to level up on AI as
much as possible. So okay fine. I have to try it.</p>
<h2 id="im-cheap">I&rsquo;m cheap</h2>
<p>I&rsquo;ve been limping at home these days just fine using the Google Gemini plus that
comes with my basic Google One account. It&rsquo;s pretty good, but I have just been
using the chat box. I also can run some local LLMs on my M3 macbook pro. That
was fun last year&hellip; but a lot has changed.</p>
<h2 id="guardrails">Guardrails</h2>
<p>I&rsquo;ll deploy a VM on one of my more isolated VLANS and kind of go from there.</p>
<h2 id="preflight">preflight</h2>
<p>Naturally my VM tooling I&rsquo;ve made in the past might need some attention. I&rsquo;m
going to install the gemini-cli client to help give me a boost with any
preflight setup to assure I can live my values a bit.</p>
<h3 id="installing-gemini">installing gemini</h3>
<p>Naturally i&rsquo;m annoyed because it&rsquo;s NPM based. Whatever..
<code>brew install gemini-cli</code>. Don&rsquo;t forget to start a new terminal session
afterwards. I haven&rsquo;t needed the cli so far&hellip; but let me assure you that the
normal chat session is helping me do my homework at a rapid pace. It took it a
bit to realize openclaw&rsquo;s config format had changed, but corrected itself
quickly.</p>
<h3 id="deploying-vm">deploying VM</h3>
<p>My existing scripts from my <a href="https://github.com/lanefu/naturallymlab">armlab</a>
naturally needed some very light refreshing. Okay I have a debian-13 VM
provisioned now. It&rsquo;s running on a VLAN I use for github runners, and has
isolation similar to a guest network.</p>
<h4 id="vm-setup">VM setup</h4>
<p>Since I&rsquo;m just testing, I installed a few basic things</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>PACKAGES<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;git curl jq podman-docker&#34;</span>
</span></span><span style="display:flex;"><span>apt update <span style="color:#f92672">&amp;&amp;</span> apt upgrade -y
</span></span><span style="display:flex;"><span>apt install -y <span style="color:#e6db74">${</span>PACKAGES<span style="color:#e6db74">}</span>
</span></span></code></pre></div><p>My hope was since podman-docker can run rootless, we could just give openclaw
docker for it to extend its capabilities, and let it run in a standard user
account.</p>
<p>Womp womp.. the lazy <code>curl mysite | bash</code> installer needs sudo.. I&rsquo;ll try harder
later. It can have sudo for now.</p>
<h2 id="openclaw-setup">Openclaw setup</h2>
<p>As mentioned I lazy-bombed with the easy installer and just mashed buttons.</p>
<p>I skipped the model setup since it was unclear how to manage using local models.</p>
<p>As far as interacting with openclaw, i just used <code>openclaw tui</code> to start with</p>
<h3 id="lmstudio">lmstudio</h3>
<p>I really wanted to just try melting my macbook before melting my wallet.
Unfortunately it only has 18GB of RAM.. but I tried anyway with some smaller
models.</p>
<p>I updated my network firewall to permit traffic from Openclaw&rsquo;s VLAN to the
lmstudio server port on my macbook. In lmstudio, there wasn&rsquo;t much work, just a
flag, to tell the server to listen on the LAN instead of 127.0.0.1.</p>
<p>To get openclaw to talk to LMstudio. I ended up with a config like this</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;models&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;providers&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;lmstudio&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;baseUrl&#34;</span>: <span style="color:#e6db74">&#34;http://MACBOOK_IP:1234/v1&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;apiKey&#34;</span>: <span style="color:#e6db74">&#34;lm-studio&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;api&#34;</span>: <span style="color:#e6db74">&#34;openai-responses&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;models&#34;</span>: [
</span></span><span style="display:flex;"><span>          {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;qwen/qwen3-4b-thinking-2507&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;qwen3&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;reasoning&#34;</span>: <span style="color:#66d9ef">false</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;input&#34;</span>: [<span style="color:#e6db74">&#34;text&#34;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;cost&#34;</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;input&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;output&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheRead&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheWrite&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;contextWindow&#34;</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;maxTokens&#34;</span>: <span style="color:#ae81ff">4096</span>
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;agents&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;defaults&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;model&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;primary&#34;</span>: <span style="color:#e6db74">&#34;lmstudio/qwen3&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;workspace&#34;</span>: <span style="color:#e6db74">&#34;/home/lane/.openclaw/workspace&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;maxConcurrent&#34;</span>: <span style="color:#ae81ff">4</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;subagents&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;maxConcurrent&#34;</span>: <span style="color:#ae81ff">8</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I tried different context window tweaks.. Openclaw requires min 16000. I enabled
some experimental quantization setting on the server side to help compress, but
ultimately after not much running&hellip; lmstudio&rsquo;s server would just fall-over and
unload the model. I&rsquo;d get maybe 5 iterations for it would die. Maybe we&rsquo;ll try
this again later.</p>
<h3 id="throwing-money-at-it">throwing money at it</h3>
<p>Based on what I see on reddit, I&rsquo;m probably not throwing enough money at it, but
I&rsquo;m going to try the <a href="https://synthetic.new">synthetic.new</a> $20/mo plan. I&rsquo;m too
afraid of getting burned on TOS, or other rate limiting to try the oauth with
Gemini. I get too much value there now.</p>
<h4 id="syntheticnew-setup">synthetic.new setup</h4>
<p>I created an account.</p>
<p>I went to billing.</p>
<p>J/K THEY&rsquo;RE OUT OF CAPACITY JOIN THE WAITING LIST.</p>

    <img src="/images/openclaw/synthetic_new_waiting_list.png"  alt="screenshot of synthetic new billing package where I signed up for the waiting list"  class="center"  style="border-radius: 8px;"  />


<h2 id="the-end">THE END</h2>
<p><code>:facepalm:</code></p>
<h2 id="trying-again">Trying again</h2>
<h3 id="all-in-with-openrouterai">All-in with Openrouter.ai</h3>
<p>Gemini led me to Openrouter.ai as another practical model provider for Openclaw.</p>
<p>I&rsquo;ll say the auto model router feature <em>is</em> pretty cool. I burned about $14
worth of Tokens arguing with openclaw claw to get things setting properly
including memory embedding, and the perplexity search capabilities in
Openrouter. It also helped me get a slackbot integration configured properly.</p>
<p>All the AI kept trying to patch the config file with an imaginary <code>--json </code>
argument. That took a while to unlearn.</p>
<p>There was also cruft in <code>.openclaw/agents/main/agent/auth-profiles.json</code> from
<code>openclaw configure</code> that I had to remove.</p>
<p>Anyway I&rsquo;ll share a working config file for using Openrouter for LLM, embedding,
and for search.</p>
<p>Note. I had to set API vars keys in <code>./openclaw/.env</code> that was the sanest way to
do it. Amusingly even tho I was using Openrouter for everything&hellip; I had to set
an anthropic and openapi key as well just for some of the things to work
correctly through open router. That&rsquo;s not a great explanation&hellip;. but it&rsquo;s a
breadcrumb for ya!</p>
<p><code>.openclaw/.env</code> file</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>OPENAI_API_KEY<span style="color:#f92672">=</span>YOUR_OPEN_ROUTER_KEY
</span></span><span style="display:flex;"><span>OPENAI_API_BASE<span style="color:#f92672">=</span>https://openrouter.ai/api/v1
</span></span><span style="display:flex;"><span>OPENROUTER_API_KEY<span style="color:#f92672">=</span>YOUR_OPEN_ROUTER_KEY
</span></span><span style="display:flex;"><span>ANTHROPIC_API_KEY<span style="color:#f92672">=</span>YOUR_OPEN_ROUTER_KEY
</span></span></code></pre></div><p><code>.openclaw/openclaw.json</code> config. There&rsquo;s cruft, but hopefully helpful. BTW I
got burned pulling json snippets from Openclaw&rsquo;s documentation website, because
their documentation syntax formatting for json removes the quotes making it
invalid json you can&rsquo;t paste&hellip;. yet again.. Thanks gemini</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;meta&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lastTouchedVersion&#34;</span>: <span style="color:#e6db74">&#34;2026.2.6-3&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lastTouchedAt&#34;</span>: <span style="color:#e6db74">&#34;2026-02-08T22:08:22.838Z&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;wizard&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lastRunAt&#34;</span>: <span style="color:#e6db74">&#34;2026-02-08T18:05:00.747Z&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lastRunVersion&#34;</span>: <span style="color:#e6db74">&#34;2026.2.6-3&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lastRunCommand&#34;</span>: <span style="color:#e6db74">&#34;configure&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lastRunMode&#34;</span>: <span style="color:#e6db74">&#34;local&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;auth&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;profiles&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;openrouter:default&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;provider&#34;</span>: <span style="color:#e6db74">&#34;openrouter&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;mode&#34;</span>: <span style="color:#e6db74">&#34;api_key&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;models&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;providers&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;openrouter&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;baseUrl&#34;</span>: <span style="color:#e6db74">&#34;https://openrouter.ai/api/v1&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;apiKey&#34;</span>: <span style="color:#e6db74">&#34;YOUR_OPEN_ROUTER_KEY&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;api&#34;</span>: <span style="color:#e6db74">&#34;openai-responses&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;models&#34;</span>: [
</span></span><span style="display:flex;"><span>          {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;deepseek/deepseek-r1&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;DeepSeek R1&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;reasoning&#34;</span>: <span style="color:#66d9ef">false</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;input&#34;</span>: [<span style="color:#e6db74">&#34;text&#34;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;cost&#34;</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;input&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;output&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheRead&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheWrite&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;contextWindow&#34;</span>: <span style="color:#ae81ff">164000</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;maxTokens&#34;</span>: <span style="color:#ae81ff">8192</span>
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>          {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;meta-llama/llama-3.3-70b-instruct&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;Llama 3.3 70B&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;reasoning&#34;</span>: <span style="color:#66d9ef">false</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;input&#34;</span>: [<span style="color:#e6db74">&#34;text&#34;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;cost&#34;</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;input&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;output&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheRead&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheWrite&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;contextWindow&#34;</span>: <span style="color:#ae81ff">131072</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;maxTokens&#34;</span>: <span style="color:#ae81ff">8192</span>
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>          {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;google/gemini-2.0-flash-exp:free&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;Gemini 2.0 Flash (Free)&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;reasoning&#34;</span>: <span style="color:#66d9ef">false</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;input&#34;</span>: [<span style="color:#e6db74">&#34;text&#34;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;cost&#34;</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;input&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;output&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheRead&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">&#34;cacheWrite&#34;</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;contextWindow&#34;</span>: <span style="color:#ae81ff">1048576</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;maxTokens&#34;</span>: <span style="color:#ae81ff">8192</span>
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;agents&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;defaults&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;model&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;primary&#34;</span>: <span style="color:#e6db74">&#34;openrouter/auto&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;fallbacks&#34;</span>: [
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;openrouter/meta-llama/llama-3.3-70b-instruct&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#e6db74">&#34;openrouter/google/gemini-2.0-flash-exp:free&#34;</span>
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;models&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;openrouter/auto&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;alias&#34;</span>: <span style="color:#e6db74">&#34;OpenRouter&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;memorySearch&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;provider&#34;</span>: <span style="color:#e6db74">&#34;openai&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;remote&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;baseUrl&#34;</span>: <span style="color:#e6db74">&#34;https://openrouter.ai/api/v1&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;apiKey&#34;</span>: <span style="color:#e6db74">&#34;YOUR_OPEN_ROUTER_KEY&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;model&#34;</span>: <span style="color:#e6db74">&#34;text-embedding-3-small&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;compaction&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;mode&#34;</span>: <span style="color:#e6db74">&#34;safeguard&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;maxConcurrent&#34;</span>: <span style="color:#ae81ff">4</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;subagents&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;maxConcurrent&#34;</span>: <span style="color:#ae81ff">8</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;tools&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;web&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;search&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;enabled&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;provider&#34;</span>: <span style="color:#e6db74">&#34;perplexity&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;perplexity&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;model&#34;</span>: <span style="color:#e6db74">&#34;perplexity/sonar-pro&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;messages&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;ackReactionScope&#34;</span>: <span style="color:#e6db74">&#34;group-mentions&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;commands&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;native&#34;</span>: <span style="color:#e6db74">&#34;auto&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;nativeSkills&#34;</span>: <span style="color:#e6db74">&#34;auto&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;channels&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;slack&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mode&#34;</span>: <span style="color:#e6db74">&#34;socket&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;webhookPath&#34;</span>: <span style="color:#e6db74">&#34;/slack/events&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;enabled&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;botToken&#34;</span>: <span style="color:#e6db74">&#34;botToken&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;appToken&#34;</span>: <span style="color:#e6db74">&#34;appToken&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;userTokenReadOnly&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;requireMention&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;groupPolicy&#34;</span>: <span style="color:#e6db74">&#34;allowlist&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;channels&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;SOMECHANNEL&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;enabled&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;allow&#34;</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;gateway&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;port&#34;</span>: <span style="color:#ae81ff">18789</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;mode&#34;</span>: <span style="color:#e6db74">&#34;local&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;bind&#34;</span>: <span style="color:#e6db74">&#34;lan&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;auth&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mode&#34;</span>: <span style="color:#e6db74">&#34;token&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;token&#34;</span>: <span style="color:#e6db74">&#34;MYGATEWAYTOKEN&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;tailscale&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mode&#34;</span>: <span style="color:#e6db74">&#34;off&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;resetOnExit&#34;</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;plugins&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;entries&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;slack&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;enabled&#34;</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="the-openclaw-gateway-dashboard">The Openclaw Gateway Dashboard</h3>
<p>I feel a little silly, because for much of my experimentation I just had 2
terminals panes. One with <code>openclaw tui</code> and the other with
<code>openclaw logs --follow</code>. I should have gone through the effort to use the
dashboard sooner, but it was configured for localhost only, and I didn&rsquo;t want to
port forward for some reason. I enabled it on the LAN, and then just ended up
reverse proxying as an ExternalService via an ingress on my kubernetes cluster.
It was pretty clean. I had a little trouble figuring out how to get the session
token going and validating my device from the cli, but afterwards.. the UI at
least seems cool. I didn&rsquo;t try editing the configuration from it, but you can.
Using the chat was more pleasant from a rendering perspective.</p>
<p>Poke around the UI! You can see all the pieces&hellip;. then you can ask question.</p>
<h3 id="doing-stuff">Doing stuff</h3>
<p>I yelled at it in slack and got the bot to do some simple python and bash
scripts, Run docker containers. Search the web. It was okay. I just couldn&rsquo;t get
it to <em>really</em> do any long standing work in the background as I had sort of
thought would happen. Background activities don&rsquo;t seem to follow-up right now
without continue a conversation.. I was hoping I&rsquo;d see more automatic messages
in slack that weren&rsquo;t direct responses. It could be a configuration issue of
course.</p>
<p>Cron jobs have the same thing&hellip; They don&rsquo;t update me unless I ask about them.</p>
<h3 id="moltbook">Moltbook</h3>
<p>I tried twice to join moltbook. Openclaw did an okay job and researching and
trying to apply. Although the first time time I think it hallucinated instead of
doing the actual work. The second time, I got an actual link and posted on
Xitter&hellip;.. BUT the verification didn&rsquo;t work, so I&rsquo;m not a able contribute to
that waste of energy yet&hellip;.. It&rsquo;s funny tho&hellip; I was looking for things to for
Openclaw to do.. and was struggling to find something simple&hellip;. Moltbook
suddenly made sense.</p>
<h2 id="takeways">Takeways</h2>
<p>The real winner here is the basic &ldquo;plus&rdquo; tier of Google Gemini. It continues to
be really good&hellip; It helped me solve configuration issues for openclaw, generate
some k8s manifests I needed, and research all sorts of things&ndash;at a low cost.</p>
<p>To get real value out of openclaw I think I&rsquo;d need to try a lot harder&hellip; BUT it
seems like you also need to really double-down on it. Like the guy that used the
$200/mo OpenAI package as the &ldquo;best value.&rdquo; I believe them to, but I&rsquo;m not in
the mood to go all-in on this. I can&rsquo;t pay $200/mo to advance hobby/science
projects&hellip; it doesn&rsquo;t make sense. I&rsquo;m better off just continuing to use the
basic AI accelerators already available to me&hellip;. And maybe one day&hellip;.. I won&rsquo;t
feel this existential pressure to do technology in a hurry, and I can just go
slow again&hellip;maybe.</p>
<p>So I spent over half my weekend and $25 in tokens tinkering with this, and if
I&rsquo;m smart&hellip; I&rsquo;ll shut down the VM and let a different tier of technologist play
with it. Basically I setup a slack proxy to let an LLM agent run a docker
container.</p>
<p>Maybe next weekend I&rsquo;ll go in the garage.</p>
]]></content></item><item><title>Moving out -- blog rehoming</title><link>https://blog.lane-fu.com/posts/2025/12/moving-out--blog-rehoming/</link><pubDate>Mon, 01 Dec 2025 19:47:06 -0500</pubDate><guid>https://blog.lane-fu.com/posts/2025/12/moving-out--blog-rehoming/</guid><description>&lt;p>I&amp;rsquo;ve managed to power down 2 of my 3 x86 servers in the colo. This blog (now
moved) and my nephew&amp;rsquo;s Minecraft servers seem to be the only remaining workloads
on the remaining x86 node.&lt;/p>
&lt;p>The blog&amp;rsquo;s new home falls under my &amp;ldquo;legacy&amp;rdquo; static hosting model. Aka content is
stored in a share on my synology, I then expose the content to an NFS mounted
folder from an Nginx pod. Traffic is back-hauled through haproxy on my Hetzner
VPS, through Wireguard to my &amp;ldquo;external&amp;rdquo; ingress-nginx controller, which is
configured to support the PROXY protocol.&lt;/p></description><content type="html"><![CDATA[<p>I&rsquo;ve managed to power down 2 of my 3 x86 servers in the colo. This blog (now
moved) and my nephew&rsquo;s Minecraft servers seem to be the only remaining workloads
on the remaining x86 node.</p>
<p>The blog&rsquo;s new home falls under my &ldquo;legacy&rdquo; static hosting model. Aka content is
stored in a share on my synology, I then expose the content to an NFS mounted
folder from an Nginx pod. Traffic is back-hauled through haproxy on my Hetzner
VPS, through Wireguard to my &ldquo;external&rdquo; ingress-nginx controller, which is
configured to support the PROXY protocol.</p>
<p>When the blog was hosted in Lanecloud, it was stored in Minio, and reverse
proxied via Nginx. At the time, I had spent a ton of time with the Nginx
configuration so that it hid any hint of Minio. It was probably unnecessary, but
it was a fun exercise.</p>
<p>Rclone supports SMB, which really simplified moving the blog. I just changed my
rclone target in my blog script to use an SMB target on my Synology instead of
the rclone job to Minio. Pretty rad.</p>
]]></content></item><item><title>RIP Lanecloud</title><link>https://blog.lane-fu.com/posts/2025/11/rip-lanecloud/</link><pubDate>Fri, 14 Nov 2025 17:39:45 -0500</pubDate><guid>https://blog.lane-fu.com/posts/2025/11/rip-lanecloud/</guid><description>&lt;p>We had a good run&amp;ndash;over 4 years of service. The equipment was racked in the Colo
in August of 2021. I had some fun and learned a lot. I hope my friends did too.&lt;/p>
&lt;h3 id="shout-outs">Shout outs!&lt;/h3>
&lt;ul>
&lt;li>My rackmate, @ssh &amp;ndash; thanks for getting me into this and being an awesome
network engineer.&lt;/li>
&lt;li>Data Center Dude Josh &amp;ndash; thanks for all the service, support and friendship.&lt;/li>
&lt;li>Britt &amp;ndash; thanks for your support, enthusiasm, and hardware!&lt;/li>
&lt;li>Lanecloud customers &amp;ndash; thanks for being part of the project!&lt;/li>
&lt;/ul>
&lt;h3 id="so-why-now">So why now?&lt;/h3>
&lt;p>A few factors&amp;hellip; Mainly it&amp;rsquo;s been time for a while. I&amp;rsquo;ve been slowly working on
v2, but it&amp;rsquo;s a lot of work. Building something that is &lt;em>serious&lt;/em> and meant for
other people to use is a large undertaking. It kind of stopped being fun, and
all the prerequisite activities just keep taking me away from working on the
things I&amp;rsquo;d rather work on. Sounds similar to TCO and Opportunity cost narratives
we use to justify going to the cloud doesn&amp;rsquo;t it?&lt;/p></description><content type="html"><![CDATA[<p>We had a good run&ndash;over 4 years of service. The equipment was racked in the Colo
in August of 2021. I had some fun and learned a lot. I hope my friends did too.</p>
<h3 id="shout-outs">Shout outs!</h3>
<ul>
<li>My rackmate, @ssh &ndash; thanks for getting me into this and being an awesome
network engineer.</li>
<li>Data Center Dude Josh &ndash; thanks for all the service, support and friendship.</li>
<li>Britt &ndash; thanks for your support, enthusiasm, and hardware!</li>
<li>Lanecloud customers &ndash; thanks for being part of the project!</li>
</ul>
<h3 id="so-why-now">So why now?</h3>
<p>A few factors&hellip; Mainly it&rsquo;s been time for a while. I&rsquo;ve been slowly working on
v2, but it&rsquo;s a lot of work. Building something that is <em>serious</em> and meant for
other people to use is a large undertaking. It kind of stopped being fun, and
all the prerequisite activities just keep taking me away from working on the
things I&rsquo;d rather work on. Sounds similar to TCO and Opportunity cost narratives
we use to justify going to the cloud doesn&rsquo;t it?</p>
<p>Another reason I had kept going was that we had an opportunity to host a big
shiny ARM server. Unfortunately after months and months of correspondence, and 2
different vendors, we just can&rsquo;t seem to get it over the line. It&rsquo;s not worth
waiting and keeping the lights on for it anymore.</p>
<h3 id="where-am-i-gonna-go">Where am I gonna go?</h3>
<p>Most of my footprint will probably move to home and Friendnet. NetCup ARM VPS&rsquo;s
are looking really appealing. I might even setup a full k8s cluster out there at
some point. I also like HostHatch.</p>
<h3 id="when-are-things-going-offline">When are things going offline?</h3>
<p>I hope to have things fully powered down Mid December 2025.</p>
]]></content></item><item><title>Running Lazyvim as an Alias</title><link>https://blog.lane-fu.com/posts/2025/09/running-lazyvim-as-an-alias/</link><pubDate>Sat, 20 Sep 2025 11:04:01 -0400</pubDate><guid>https://blog.lane-fu.com/posts/2025/09/running-lazyvim-as-an-alias/</guid><description>&lt;p>My sysadmin roots kept me living in default vim inside screen and tmux for
years. Fortunately as my roles evolved, the world evolved, and I have also
evolved as an engineer. I needed to level up to a better editor. Every time I
tried vscode, it killed me. Every time I tried to rice vim or neovim myself, it
was over my head. I discovered &lt;a href="https://www.lazyvim.org">LazyVim&lt;/a> and it was the
boost I needed. I&amp;rsquo;ve been able to do much more advanced editing tasks and it&amp;rsquo;s
quite fast.&lt;/p></description><content type="html"><![CDATA[<p>My sysadmin roots kept me living in default vim inside screen and tmux for
years. Fortunately as my roles evolved, the world evolved, and I have also
evolved as an engineer. I needed to level up to a better editor. Every time I
tried vscode, it killed me. Every time I tried to rice vim or neovim myself, it
was over my head. I discovered <a href="https://www.lazyvim.org">LazyVim</a> and it was the
boost I needed. I&rsquo;ve been able to do much more advanced editing tasks and it&rsquo;s
quite fast.</p>
<p>However, I still wanted to keep my default neovim for when I really wanted to
get in and out of an editor fast. I decided to launch LazyVim by typing <code>lvim</code>
and leave my standard <code>vim</code> and <code>nvim</code>.</p>
<p>Fortunately neovim has a concept called <code>NVIM_APPNAME</code> that lets you load the
app from an alternate configuration directory.</p>
<p>Pretty simple stuff.. Checkout lazyvim into <code>~/.config/lvim</code> and make a command
alias for lvim setting the <code>NVIM_APPNAME</code> variable</p>
<p>Here&rsquo;s the key snippet from my
<a href="https://gist.github.com/lanefu/63881e9dc22cdb2607d3c3c22a64fb13">lazyvim github gist</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone https://github.com/LazyVim/starter ~/.config/lvim
</span></span><span style="display:flex;"><span><span style="color:#75715e">### put this in your shell profile</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">### you need neovim 0.9.newish for the NVIM_APPNAME param to work..</span>
</span></span><span style="display:flex;"><span>alias lvim<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;NVIM_APPNAME=lvim nvim&#34;</span>
</span></span></code></pre></div>]]></content></item><item><title>Opensource Citizenship: Sending fixes to a mature project: Ansible Netbox Upstream Dance</title><link>https://blog.lane-fu.com/posts/2025/09/opensource-citizenship-sending-fixes-to-a-mature-project-ansible-netbox-upstream-dance/</link><pubDate>Fri, 19 Sep 2025 09:27:23 -0400</pubDate><guid>https://blog.lane-fu.com/posts/2025/09/opensource-citizenship-sending-fixes-to-a-mature-project-ansible-netbox-upstream-dance/</guid><description>&lt;p>Even the title is a mouthful, but that might be how you need to write your
commit messages depending on the project.&lt;/p>
&lt;p>So this is a blog post I started in April&amp;hellip; Now it&amp;rsquo;s nearly the end of
September. I&amp;rsquo;ll keep it short and sweet just so I can close this out.&lt;/p>
&lt;p>Contributing to mature open source projects isn&amp;rsquo;t a fast process. It requires
diligence and patience on your part, and you may be surprised how much work is
involved beyond the simple bug fix you actually implemented.&lt;/p></description><content type="html"><![CDATA[<p>Even the title is a mouthful, but that might be how you need to write your
commit messages depending on the project.</p>
<p>So this is a blog post I started in April&hellip; Now it&rsquo;s nearly the end of
September. I&rsquo;ll keep it short and sweet just so I can close this out.</p>
<p>Contributing to mature open source projects isn&rsquo;t a fast process. It requires
diligence and patience on your part, and you may be surprised how much work is
involved beyond the simple bug fix you actually implemented.</p>
<p>The important thing to remember is that what can feel like turnstiles,
complexity, and unnecessary prerequisites are really there to protect the sanity
of the under-compensated developers of the project. In the case of the
contribution that inspired this post, it teaches you a lot about what good looks
like for testing, changelogs, and managing releases.</p>
<p>At this point&ndash;about 18 months ago, I ran into a bug with the Ansible Netbox
inventory plugin. I run my netbox server under subpath rather than at the root
path. This <em>IS</em> a
<a href="https://netboxlabs.com/docs/netbox/configuration/system/#base_path">supported configuration with netbox</a>
itself, its just less supported. (even now the swagger UI for the netbox API
generates the wrong urls). After an update, how the inventory plugin derived the
api paths from OpenAPI broke my ability to use it. Borrowing from inspiration in
one of the other modules in the collection, I was able to implement a simple fix
in an evening and resume doing ansible+netbox things.</p>
<p>As mentioned the fix wasn&rsquo;t too bad and my buddy encouraged me to upstream it. I
remember seeing that open a PR was a bit involved, so I procrastinated&ndash;for
about a year. Honestly I expected someone else would have probably resolved the
issue already, but that wasn&rsquo;t the case. I conceded, and put on my good open
source citizen hat.</p>
<p>This was the part of the blog that was supposed to highlight in detail my
colorful weekend-long journey to get my PR in the correct place, but I don&rsquo;t
remember it quite as well now. I&rsquo;m just gonna make a simple list that might
paint the picture:</p>
<ul>
<li>Find correct issue references</li>
<li>Set up local netbox and local test environment</li>
<li>Get test tools to pass in local test environment</li>
<li>Hunt down issue references</li>
<li>Figure out correct way to create a changelog fragment and commit</li>
<li>Bang on my PR until the required tests in the pipeline passed</li>
</ul>
<p>Each of those pieces proved to be a bit time consuming for me, but I got through
it all bit by bit.</p>
<p>I&rsquo;m happy to say my
<a href="https://netboxlabs.com/docs/netbox/configuration/system/#base_path">PR to the ansible-netbox inventory plugin</a>
was accepted and merged. As of this writing, there hasn&rsquo;t been a new release, so
you&rsquo;ll have to install from the master branch.</p>
<p>I&rsquo;m looking forward to seeing my fix listed in the next release changelog. It
was a bit of a slow process, but that&rsquo;s one of many things that makes open
source successful. It&rsquo;s great all the tooling is in place for thorough testing
so we can confidently know my change doesn&rsquo;t break things for others.</p>
]]></content></item><item><title>100 days of homelab Activity Log</title><link>https://blog.lane-fu.com/posts/2025/03/100-days-of-homelab-activity-log/</link><pubDate>Thu, 06 Mar 2025 20:46:20 -0500</pubDate><guid>https://blog.lane-fu.com/posts/2025/03/100-days-of-homelab-activity-log/</guid><description>&lt;p>I&amp;rsquo;ll just log mundane things here and update this post. Cool stuff will get a
dedicated post. Do I go top bottom, or bottom top? I guess we&amp;rsquo;ll find out.&lt;/p>
&lt;h2 id="day-002-20250306">day 002 20250306&lt;/h2>
&lt;ul>
&lt;li>made this blog post (baby steps okay?)&lt;/li>
&lt;li>logged into one of my ingress boxes and looked at logs. 99% bot traffic. no
surprise.&lt;/li>
&lt;li>one of my lanecloud hypervisor hosts has 550 days of uptime&amp;hellip; that&amp;rsquo;s
something I guess.&lt;/li>
&lt;li>started on an ansible role to add custom startup scripts to clammy-ng&lt;/li>
&lt;li>got distracted with some hugo theme tweaks that I couldn&amp;rsquo;t quite make work&lt;/li>
&lt;/ul>
&lt;h2 id="day-003-20250308">day 003 20250308&lt;/h2>
&lt;p>Ended up being kind of an Armbian Day&lt;/p></description><content type="html"><![CDATA[<p>I&rsquo;ll just log mundane things here and update this post. Cool stuff will get a
dedicated post. Do I go top bottom, or bottom top? I guess we&rsquo;ll find out.</p>
<h2 id="day-002-20250306">day 002 20250306</h2>
<ul>
<li>made this blog post (baby steps okay?)</li>
<li>logged into one of my ingress boxes and looked at logs. 99% bot traffic. no
surprise.</li>
<li>one of my lanecloud hypervisor hosts has 550 days of uptime&hellip; that&rsquo;s
something I guess.</li>
<li>started on an ansible role to add custom startup scripts to clammy-ng</li>
<li>got distracted with some hugo theme tweaks that I couldn&rsquo;t quite make work</li>
</ul>
<h2 id="day-003-20250308">day 003 20250308</h2>
<p>Ended up being kind of an Armbian Day</p>
<ul>
<li>submitted the removal of a RK3568 bugfix patch since it got merged into LTS
kernels and was conflicting on build.</li>
<li>Ran a bunch of glmark2 and sbcbench marks on boards comparing kernels. No real
conclusions other than I think an RK3588 DMA patch someone added had a slight
performance regression.</li>
</ul>
<h2 id="day-004-20250309">day 004 20250309</h2>
<ul>
<li>
<p>My Synology backups that goto my eSata drive have been dead for a month. Got
the drive online&hellip; now says its full. Really need to add back push
notifications for that. More work to be done to get this resolved.</p>
</li>
<li>
<p>Made a
<a href="https://github.com/lanefu/armlab/blob/main/playbooks/provision_vm_interactive.yml">new playbook</a>
for my
<a href="https://github.com/lanefu/ansible-collection-armlab/tree/main/roles/provision_kvm_guest">Provision KVM guest ansible role</a>.
It just uses vars_prompt to make it easy to spin up a few VM in a hurry. I
like it.</p>
</li>
<li>
<p>Wasted a lot of time trying to debug performance issues with my librespeed
instance. Some of the performance problems are just VM performance sucks on
Synology VMs, even when using appropriate acceleration. Other parts of it, is
that it seems to be a known thing that nginx or ingress-nginx also seems to
introduce some performance issues when it proxies librespeed. I managed to do
some tuning that made it better, but it&rsquo;s still unable to hit wirespeed on
gigabit. I&rsquo;m extra amused that librespeed is more performant on a VM running
on my Rock 5B than it is running on my Synology DS1821+</p>
<p>Here&rsquo;s some settings for the service ingress that <em>sort of helped</em> with
nginx.. but needs more TBH</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">annotations</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">nginx.ingress.kubernetes.io/proxy-body-size</span>: <span style="color:#e6db74">&#34;21M&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">nginx.ingress.kubernetes.io/proxy-buffering</span>: <span style="color:#e6db74">&#34;on&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">nginx.ingress.kubernetes.io/proxy-buffers-number</span>: <span style="color:#e6db74">&#34;50&#34;</span>
</span></span></code></pre></div></li>
</ul>
<h2 id="day-005-20250310">day 005 20250310</h2>
<p>Just kind of did my rounds and looked at some things.</p>
<ul>
<li>Verified Synology backups are happy now that I cleaned up my external drive</li>
<li>Updated Netbox
<ul>
<li>deleted retired LET deal VMs</li>
<li>updated renewal date field on remaining LET deal VMs</li>
</ul>
</li>
<li>Verified db dumps and borgmatic backups are running on my Netbox server</li>
<li>Looked at some healthchecks.io helm charts. Would be nice to deploy that in
the future and integrate with borgmatic</li>
</ul>
<h2 id="day-006-20250311">day 006 20250311</h2>
<p>Pretty beat today so just did some fiddling. I fired up some temporary debian
pods on my synology moncluster VM, and one on my Rock 5B k3s cluster. I ran
quick <a href="https://yabs.sh">yabs.sh</a> benchmarks on them. The pod on the 6 core ARM
VM was faster in single and multithreaded than the 4 core VM on my Synology
1821+. Between this and the librespeed benchmarks, I think I&rsquo;ve convinced myself
the monocluster is moving off the Synology and onto a Rock 5B.</p>
<h3 id="wtf-is-the-monocluster">WTF is the monocluster</h3>
<p>I have 2 k3s deployments at home. The first one is the
<a href="https://github.com/lanefu/armlab/blob/main/scripts/big_bang.sh">&ldquo;armlab&rdquo; cluster</a>.
Its 6 VMs running on 3 Rock 5B boards. It&rsquo;s meant to be torn down and recreated
a lot. It has an HA control plane via kube-vip. It has metallb, cillium, and
some basic service deployed via flux. I really wanted to figure out using BGP in
the HA cluster before deploying to it, but I decided I should do that later and
first just focus on getting my nomad cluster.</p>
<p>The monocluster was my KISS approach to just focus on a &ldquo;best practices&rdquo; IAC
setup for k8s with fluxcd&hellip;and focus on bootstrapping all the extra things like
external-dns, external-secrets, etc. One detail is that I was also trying to
design it as a solution I could use to run on a single node VPS.. It took me a
while to solve for getting nginx to listen on port 80,443 node ports but I did
it. I&rsquo;ve been redeploying it like crazy as well while I add functionality, but
it&rsquo;s time to set up it&rsquo;s final (temporary) home.. and move stuff over it to it.</p>
<h2 id="day-007-20250313">day 007 20250313</h2>
<p>I found one of my rock-5b boards that I used as an armbian build node a year or
so ago with the Googalator kernel. It has a nice NVME in it. It&rsquo;s a great fit
for the home of my monocluster. So I decided to build an image and get it
installed&hellip; Naturally I hit some unexpected curveballs</p>
<ul>
<li>My other Synology cache drive alerted that it had 1% lifespan&hellip;&hellip; I had to
replace it.. Now I&rsquo;m running my cache on a pair of $20 nvmes&hellip; RIP crucial P3</li>
<li>I booted edge kernel 6.14.0-rc4&hellip; it was weird&hellip; the nvme was intermittent.
And eventually disappeared&hellip; I went back and forth a few times and then
decided to build -current aka LTS kernel 6.12.y.</li>
<li>worked better.. but something with armbian-install or uboot on SPI flash isn&rsquo;t
booting from NVME.. i don&rsquo;t feel like debugging.</li>
<li>just using sdcard for bootloader for now. seems to work.</li>
</ul>
<h2 id="day-008-20250314">day 008 20250314</h2>
<p>I&rsquo;m surprised about my burst of productivity given I&rsquo;ve been on like 4 hours of
sleep today.</p>
<ul>
<li>The nvme boot problem seem to partially have somethign to do with the nvme
drive I was using.. it would init fine on a reboot, but not on a cold boot.
Storage controllers and SBCs can be picky. I pulled a 512 Skyhinx out of an
external enclosure and now it&rsquo;s happy.</li>
<li>I re-installed monocluster as bare metal k3s on the above mentioned Rock 5B.</li>
<li>I also tested libre speed&hellip; looks good!</li>
</ul>
<pre tabindex="0"><code>root@ronny-1:~# librespeed-cli --skip-cert-verify --local-json librespeed.json --concurrent 2 --server 1
Using local JSON server list: librespeed.json
Selected server: mtest [librespeed.mtest]
You&#39;re testing from: {&#34;processedString&#34;:&#34;172.17.20.115 - private IPv4 access&#34;,&#34;rawIspInfo&#34;:&#34;&#34;}
Ping: 0.64 ms	Jitter: 0.38 ms
Download rate:	925.08 Mbps
Upload rate:	953.28 Mbps
</code></pre><h2 id="day-009-20250315">day 009 20250315</h2>
<p>Time to <em>start</em> getting some actual workloads moved over&hellip;. or in my case..
test some workloads.</p>
<p>I focused on some basic nginx servers that are just directory indexes for data
on an NFS mount. Was a great starting point.</p>
<p>I lost a lot of time trying to use
<a href="https://artifacthub.io/packages/helm/bjw-s/app-template">app-template</a> to do
the things. It&rsquo;s cool, but also extremely overkill and ended up being a bad fit
for my use case.</p>
<p>I dumbed things down to unified manifest file for each server + service. I took
advantage of flux&rsquo;s <code>ConfigMapGenerator</code> to make my configuration hashed and
cause pods to redeploy when I update. Works well.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">kustomize.config.k8s.io/v1beta1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Kustomization</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">web-static</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">./namespace.yaml</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">./nginx-linuxmirror.yaml</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">./nginx-otherserver.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">configMapGenerator</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx-linuxmirror</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">files</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">default.conf=nginx-configs/default-linuxmirror.conf</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx-default</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">files</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">default.conf=nginx-configs/default.conf</span>
</span></span></code></pre></div><h2 id="day-010-20250316">day 010 20250316</h2>
<ul>
<li>moved the monocluster rock5b into my rack</li>
<li>wired the fan on the heatsink to 3.3v for now</li>
<li>ordered some Molex Picoblade 1.25mm connector crimps so that I can wire the
fan to the actual fan header later</li>
<li>naturally the switch port i used was wrong and i had to fix the PVID before I
was pulling the properly assigned static DHCP address.</li>
</ul>
<h2 id="day-011-20250317">day 011 20250317</h2>
<h3 id="a-rude-awaking">A rude awaking</h3>
<p>Checked my email in the morning and saw an abuse report from Hetzner in my
inbox. Turns out people were exploiting an old pastebin server I&rsquo;d setup a long
time ago for Armbian-related stuff. My hetzner box has haproxy doing TCP reverse
proxying over a wireguard tunnel to my fabio load balancer for my nomad cluster.
I stupidly have a mixture of public and internal services running on it.
Anyway.. I terminated the service and ripped out the DNS records&hellip; Nobody was
using it&ndash;including myself. Not how I wanted to start my week, but I good
reminder to spend some more energy on my security posture. Looks like a bit of
extra bandwidth got burned on my hetzner account as well. They changed policies
from 20TB included a month to 1TB :( Boo&hellip;. but lets be real, 20TB of bandwidth
a month in Ashburn for $5 a month is too good to be true.</p>
<p>Hopefully a better homelab day tomorrow.</p>
<h2 id="day-012-20250319">day 012 20250319</h2>
<p>My rackmate is working on a new core router. We&rsquo;re upgrading hardware and going
from vyos 1.3 to 1.4. I had to re-write my NAT rules. Also trying to use a dummy
interface for my NAT interface since sometimes we have multiple WAN links..
Hopefully this will simplify things. Regardless I put the translation interface
in an interface group. Hopefully that will make it easy to move things around in
a pinch</p>
<h2 id="day-013-20250324">day 013 20250324</h2>
<p>Mostly poked around k9s and grafana today. Just wanted keep up the rhythm at
least. I upgraded k3s on my armcluster. It was a minor update or 2 behind.
Ansible did all the work of course.</p>
<h2 id="day-014-20250406">day 014 20250406</h2>
<p>Had some other stuff going on the past 2 weeks, but I haven&rsquo;t given up.</p>
<p>I did my normal kernel build and update routine for my router. I spent a little
bit of time trying to see if I could speed up the reconciliation time of my
fluxcd deployment, but no solutions yet. I have a dependency chain that get held
up waiting on external-secrets to reconcile back into a ready state. The
annoying this is external-secrets leaves a ready state on any update I push.</p>
<h2 id="day-015-20250412">day 015 20250412</h2>
<p>Busy day. Changed from NVME cached LVM to ZFS on one of my lanecloud servers. It
was quite a bit to unwind.. had to move VMs. make checklists.. move filesytems,
backup stuff. etc. The box has 256 gigs of RAM so ZFS cheats performance pretty
well on top of the underlying 4 spindles. I made my 2 NVME drives its own mirror
pool. The 4 rust drives I configured in the ZFS raid10 equivalent:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SDA<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;wwn-0x5000c50091843eae&#34;</span>
</span></span><span style="display:flex;"><span>SDB<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;wwn-0x5000c50091906b31&#34;</span>
</span></span><span style="display:flex;"><span>SDC<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;wwn-0x5000c50092509d76&#34;</span>
</span></span><span style="display:flex;"><span>SDD<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;wwn-0x5000c500926006cc&#34;</span>
</span></span><span style="display:flex;"><span>zpool create rustpool <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -o ashift<span style="color:#f92672">=</span><span style="color:#ae81ff">12</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -o autotrim<span style="color:#f92672">=</span>on <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -O compression<span style="color:#f92672">=</span>zstd <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -O dedup<span style="color:#f92672">=</span>off <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -O xattr<span style="color:#f92672">=</span>sa <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -O acltype<span style="color:#f92672">=</span>posixacl <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -O atime<span style="color:#f92672">=</span>on <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -O relatime<span style="color:#f92672">=</span>on <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -O sync<span style="color:#f92672">=</span>standard <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    mirror <span style="color:#e6db74">${</span>SDA<span style="color:#e6db74">}</span> <span style="color:#e6db74">${</span>SDB<span style="color:#e6db74">}</span> mirror <span style="color:#e6db74">${</span>SDC<span style="color:#e6db74">}</span> <span style="color:#e6db74">${</span>SDD<span style="color:#e6db74">}</span>
</span></span></code></pre></div><h2 id="day-016-20250413">day 016 20250413</h2>
<p>Around 18 months ago I ran into a bug with the Ansible Netbox inventory plugin
when querying my netbox server that runs on a subpath. I made a local fix, but
never upstreamed it. This weekend I decided to upstream it. Rebasing my patch
just worked. That was cool. Doing the actual dance to properly submit the patch
to GitHub was more involved. I was going to write a dedicated blog post on it,
but maybe later.. or not at all. I&rsquo;m not complaining at all, it&rsquo;s just part of
open source&hellip; and projects need good PR hygiene and process so that devs can
not be beat down.</p>
<h2 id="day-017-20250415">day 017 20250415</h2>
<p>Woohoo my bugfix for the
<a href="https://github.com/netbox-community/ansible_modules/pull/1407#pullrequestreview-2770032439">Ansible Netbox Inventory Plugin PR</a>
was accepted. FOSS Works! :P</p>
<p>I haven&rsquo;t redeployed the VMs on the server that got the ZFS switch. My nomad
tasks I stopped disappeared. And the particular VMs running were from my oldest
iteration of the lanecloud IAC. TLDR; I have to bugfix the playbooks to get them
to redeploy&hellip;.. or I can follow through with switching to libvirt deployments.
I opted to modernize&hellip; Let&rsquo;s just say I&rsquo;m moving slow.</p>
<h2 id="day-018-20250505">day 018 20250505</h2>
<p>Man.. really spacing this stuff out&hellip;. in my defense last week I was on
vacation and I made sure to avoid computering.</p>
<p>Just a basic maintenance night to kind of get the juices flowing.. Some
apt-updates and built some fresh 6.14 kernels for my RK3588 cluster</p>
<p>Apt update on my poor helios4 trixie box took forever&hellip; but that&rsquo;s normal. The
Helios 4 is still a champ!</p>
<h2 id="day--2025-q2-q3-update">day ??? 2025 Q2-Q3 update</h2>
<p>Clearly I lost track of things.. Daily routines are hard for me I&rsquo;m bursty in my
projects by default.</p>
<p>I did check off several milestones&hellip; each with their own deep rabbit holes.</p>
<ul>
<li>I finally fully migrated my home things to my k3s monocluster
<ul>
<li>powered down my nomad cluster! RIP: i think it was up for at least 7 years</li>
<li>Forked a
<a href="https://github.com/lanefu/cert-manager-webhook-dns-lexicon">certmanager-webhook</a>
that used DNS lexicon.
<ul>
<li>Reworked the pipeline to build multiarch containers &ndash; quickly.</li>
</ul>
</li>
</ul>
</li>
<li>Added
<a href="https://github.com/lanefu/clammy-ng/blob/main/host_vars/nanopi-r5s.yml#L236-L274">unbound dns with blocklist support</a>
to clammy-ng</li>
<li>Finally updated my 4 year old homeassistant install
<ul>
<li>lots of breaking config tweaks i had to catch up</li>
<li>also some fiddling with some tasmota devices that had reset</li>
</ul>
</li>
<li>Lanecloud Modernization Prep-work
<ul>
<li>Created an
<a href="https://github.com/lanefu/ansible-collection-armlab/tree/main/roles/netbox_register_vm">ansible role for VM registration in netbox</a>
<ul>
<li>lots of rabbit holes again here</li>
<li>this allows me to completely decouple VM registration / configuration
properties from the actual provisioning code</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>That was quite a bit of work&hellip; I think i&rsquo;ll allow my next day to be day 50</p>
]]></content></item><item><title>100 days of homelab delayed start -- day 1</title><link>https://blog.lane-fu.com/posts/2025/03/100-days-of-homelab-delayed-start--day-1/</link><pubDate>Wed, 05 Mar 2025 20:41:12 -0500</pubDate><guid>https://blog.lane-fu.com/posts/2025/03/100-days-of-homelab-delayed-start--day-1/</guid><description>&lt;p>I&amp;rsquo;m 2 years late to the party for the whole
&lt;a href="https://100daysofhomelab.com">#100daysofhomelab&lt;/a> thing. That&amp;rsquo;s okay. I decided
to start now because I have a huge homecloud and
&lt;a href="https://blog.lane-fu.com/posts/2024/07/a-lanecloud-explanation/">lanecloud&lt;/a> backlog that just hasn&amp;rsquo;t
moved forward. Additionally, I&amp;rsquo;m going to help host some more Armbian stuff in
lanecloud, so I need to make good on my commitments.&lt;/p>
&lt;p>I figured interacting with Hugo was a good warm-up for a workday evening. I
always have to kind of re-remember how to interact with it. I&amp;rsquo;m also not very
good at image processing. So lets try that again.&lt;/p></description><content type="html"><![CDATA[<p>I&rsquo;m 2 years late to the party for the whole
<a href="https://100daysofhomelab.com">#100daysofhomelab</a> thing. That&rsquo;s okay. I decided
to start now because I have a huge homecloud and
<a href="/posts/2024/07/a-lanecloud-explanation/">lanecloud</a> backlog that just hasn&rsquo;t
moved forward. Additionally, I&rsquo;m going to help host some more Armbian stuff in
lanecloud, so I need to make good on my commitments.</p>
<p>I figured interacting with Hugo was a good warm-up for a workday evening. I
always have to kind of re-remember how to interact with it. I&rsquo;m also not very
good at image processing. So lets try that again.</p>
<p>Here&rsquo;s a pic of the James River when it flooded earlier in the month.</p>

    <img src="/images/james_river_20250217.jpg"  alt="James River at Rockets"  class="center"  style="border-radius: 8px;"  />


<p>Good job me! Finally used the short codes correctly.</p>
<h2 id="projects">Projects</h2>
<p>Will try to just keep this high-level so the list doesn&rsquo;t look so
insurmountable. It&rsquo;s also silly that I have 2 discrete environments to worry
about, but that&rsquo;s just how it is.</p>
<h3 id="homecloud---migrate-my-nomad-services-to-kubernetes">Homecloud - migrate my Nomad services to Kubernetes</h3>
<p>I&rsquo;ve basically done the hard part. I&rsquo;ve got a single node x86 variant of
opinionated
<a href="https://github.com/lanefu/armlab/blob/main/scripts/big_bang.sh">armlab</a> k3s
build.</p>
<p>Core attributes of the monocluster:</p>
<ul>
<li>k3s</li>
<li>cilium</li>
<li>fluxcd</li>
<li>secrets manager w/ 1password integration</li>
<li>ingress-nginx</li>
<li>external-dns</li>
<li>certmanager</li>
</ul>
<p>Over the holidays, I spent a zillion hours trying to automate all the weird
little bootstrapping steps, and solving for some of the sequencing ex. 1password
setup before external-secrets etc.</p>
<p>Also really the biggest pain in migrating is my current Nomad setup using fabio
ingresses several different domains, and I&rsquo;m having to suck it up and migrate
things to cloudflare DNS for sanity. I still have a few more zones to move.</p>
<p>Once that&rsquo;s done, then it&rsquo;s just a matter if redeploying my existing services.</p>
<h3 id="lanecloud--move-my-vm-deployment-to-libvirt">LaneCloud &ndash; move my VM deployment to libvirt</h3>
<p>I&rsquo;ve settled on just using ansible libvirt as the most sustainable way for me to
manage VMs. I need to migrate my existing lanecloud deployments using my
homegrown nomad solution for running VMs over to just basic libvirt.</p>
<h3 id="lanecloud--personal-kubernetes-cluster">Lanecloud &ndash; personal Kubernetes cluster</h3>
<p>I&rsquo;ll stick with providing friends VPS, but I would like to just get a Kubernetes
cluster out there that uses Cilium BGP.</p>
<h3 id="lanecloud--network-shuffling">Lanecloud &ndash; network shuffling</h3>
<p>I need some more VLANs and better east-west filtering. Also want to deploy an
instance of <a href="https://github.com/lanefu/clammy-ng">clammy-ng</a> to sit between my
VLANs and our core router. It will be much easier for me to manage NAT rules
there than on our core router. We have multiple WAN links, and that means I have
to duplicate NAT rules for each path.. It&rsquo;s annoying.</p>
<h2 id="get-motivated-n-stuff">Get motivated n stuff</h2>
<p>Alright&hellip; I made this blog post. So great start, me. Good job. Also I kind of
feel better remembering all the work I already did. I just need to hammer away
at it. Hence the spirit of this whole #100daysofhomelab thing. Thanks TechnoTim
and friends.</p>
]]></content></item><item><title>Quick hints on using Kubevirt with k3s and Cilium</title><link>https://blog.lane-fu.com/posts/2024/10/quick-hints-on-using-kubevirt-with-k3s-and-cilium/</link><pubDate>Sun, 20 Oct 2024 09:27:59 -0400</pubDate><guid>https://blog.lane-fu.com/posts/2024/10/quick-hints-on-using-kubevirt-with-k3s-and-cilium/</guid><description>&lt;p>A month ago I wanted to give &lt;a href="https://kubevirt.io">kubevirt&lt;/a> a quick try as that
it&amp;rsquo;s one of the
&lt;a href="https://blog.lane-fu.com/posts/2024/07/a-lanecloud-explanation/#re-thinking-objectives">paths I was considering for refactoring lanecloud&lt;/a>.
My
&lt;a href="https://github.com/lanefu/armlab/tree/main/scripts">existing tooling for hacking on kubernetes&lt;/a>
has been a stack of k3s, kube-vip, and cilium. I wanted to build on top of that.&lt;/p>
&lt;p>Disclaimer: point of this post is just to share a few quick hints.&lt;/p>
&lt;h2 id="using-cilium-with-multus">Using cilium with multus&lt;/h2>
&lt;p>I&amp;rsquo;m just putting this here first because this was my biggest pain point.&lt;/p></description><content type="html"><![CDATA[<p>A month ago I wanted to give <a href="https://kubevirt.io">kubevirt</a> a quick try as that
it&rsquo;s one of the
<a href="https://blog.lane-fu.com/posts/2024/07/a-lanecloud-explanation/#re-thinking-objectives">paths I was considering for refactoring lanecloud</a>.
My
<a href="https://github.com/lanefu/armlab/tree/main/scripts">existing tooling for hacking on kubernetes</a>
has been a stack of k3s, kube-vip, and cilium. I wanted to build on top of that.</p>
<p>Disclaimer: point of this post is just to share a few quick hints.</p>
<h2 id="using-cilium-with-multus">Using cilium with multus</h2>
<p>I&rsquo;m just putting this here first because this was my biggest pain point.</p>
<p>Cilium has a flag called <code>cni-exclusive</code> that has to be disabled. If you don&rsquo;t
multus won&rsquo;t work, and you&rsquo;ll waste a lot of time. I eventually came across the
hint on a discord server thread where
<a href="https://technotim.live/posts/advanced-kubernetes-networking/#installing-multus">Techno Tim</a>
had been beating on multus for a non-kubevirt use case.</p>
<p>Here&rsquo;s the magic command in
<a href="https://github.com/lanefu/armlab/blob/97d83cdadb2162f6273755521d79f0c242ef234b/scripts/kubevirt/install_kubevirt.sh#L54">my multus install script</a>
that runs <em>after</em> installing cilium</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>  cilium config set cni-exclusive false
</span></span></code></pre></div><h2 id="quick-overview">Quick Overview</h2>
<p>So the whole thing with kubevirt is to be able to manage your VMs like
pods&mdash;which it does quite well. However, in that spirit, it also wants to
manage networking the same way. AKA: exposing services and do port-forwarding.
This of course is fine for many use cases, and cilium plays nice with that.</p>
<p>Another However: you might want to treat your VM like a normal VM and put it on
a VLAN and have it be fully routable / accessible. That is what having multus is
all about: it lets you have additional CNI drivers on your kubernetes cluster so
you can give your pods different types of interface&ndash;such as a bridged
interface.</p>
<p>In order to achieve that objective, there&rsquo;s several paths, but I focused on 2:</p>
<ul>
<li><a href="https://kubevirt.io/user-guide/network/net_binding_plugins/macvtap/">macvtap</a>
&ndash; This one is new, has heavy development from the kubevirt team. It has some
advantages, such as not requiring a bridge interface, it just makes a new mac
and hangs it off an existing interface. I&rsquo;m sure there&rsquo;s other advantages, but
a disadvantage is it doesn&rsquo;t work for hair-pinning traffic if you need to
reach the control host. I have some
<a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L65">scripts for setting it up</a>,
and probably could have made it work if I had found my cilium fix sooner.</li>
<li><a href="https://kubevirt.io/user-guide/network/interfaces_and_networks/#bridge">bridge</a>
&ndash; This is as tried-and-true as it gets for linux virtual machines. You make a
bridge, and new VMs attach to it. The bridge networking CNI is part of the CNI
reference suite of plugins. I ended up settling on this approach at the end.</li>
</ul>
<p>Reminder this was still just kind of a quick R&amp;D exercise, so I cut some corners
and quickly re-used some other tooling to get the job done.</p>
<p>Again the basic formula was:</p>
<ul>
<li>bridged networking</li>
<li>k3s</li>
<li>cilium &ndash; although I&rsquo;m not really taking advantage of it in this approach</li>
<li>multus</li>
<li>kubevirt</li>
</ul>
<h2 id="speedrun">Speedrun</h2>
<p>This is just stepping through parts of my
<a href="https://github.com/lanefu/armlab/tree/main/scripts/kubevirt">kubevirt install scripts folder</a>.
Remember: there&rsquo;s no substitute for hours of your own suffering, but I hope
there&rsquo;s a few hints for you here.</p>
<h3 id="k3s-base-setup">k3s base setup</h3>
<ol>
<li>Create a bridge and put your primary interface on it. Normally I&rsquo;d do this
with netplan, and frankly would have the bridge interface on a separate VLAN
from my management interface, but I just quickly did this with network
manager and created <code>bridge0</code>. Your IP may change after you do this&hellip; hence
do it first.</li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/playbooks/kubevirt/provision_kubevirt_test.yml">Install K3s</a>
and assure flannel and servicelb aren&rsquo;t configured.</li>
<li><a href="https://github.com/lanefu/armlab/blob/main/files/scripts/install_cni_reference_plugins.sh">Install bridge cni plugins</a>
&ndash; The bridge is part of a greater bundle of reference plugins, I just
filtered for bridge on the tar extraction</li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/create_test.sh#L12-L13">Copy kubeconfig from your cluster to your workstation</a></li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/create_test.sh#L17">install cilium</a>
&ndash; I followed easymode for that</li>
</ol>
<h3 id="installing-kubevirt">installing kubevirt</h3>
<ol>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L4-L15">install the virt plugin via krew</a>
&ndash; this plugin provides additional CLI comforts for interacting with the VMs</li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L17-L19">install the kubevirt operator</a></li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L25">install kubevirt crds</a></li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L36-L39">install kubevirt cdi - containerized data importer</a>
&ndash; Using CDI is a big part of doing business with kubevirt. It takes VM
images and stores them in an OCI format that&rsquo;s easier for kubevirt to deploy
without a custom storage class. You&rsquo;ll want to
<a href="https://kubevirt.io/user-guide/storage/containerized_data_importer/">read about it</a>.</li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/files/manifests/kubevirt/config/feature_gates.yaml">enable network bindings plugin feature gate</a>
&ndash; this is a kubevirt feature gate that was easiest when
<a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L51">applied post-install</a>.</li>
<li><a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L54-L57">install multus</a>
&ndash; again: take note of the cilium setting
<a href="https://github.com/lanefu/armlab/blob/ee839ffa7bc0ce80f84c44d341e03cc8662c03e4/scripts/kubevirt/install_kubevirt.sh#L56">cni-exclusive: false</a>.</li>
</ol>
<h3 id="deploying-vms">deploying VMs</h3>
<ol>
<li><a href="">create a bridge attachment definition for bridge0</a> &ndash; See
<a href="https://github.com/lanefu/armlab/blob/bd56d6442588665e44eef210635ed07ee897b4b3/files/manifests/kubevirt/bridge/bridge_attachment_definition.yaml">this manifest</a>
&ndash; the VM deployment will reference it.</li>
<li><a href="https://github.com/lanefu/armlab/blob/bd56d6442588665e44eef210635ed07ee897b4b3/files/manifests/kubevirt/armbian/dv_armbian_x86_64.yaml">create a data volume</a>
&ndash; CDI will import this image and create a datavolume that the VM will use</li>
<li>create a virtual machine &ndash;
<a href="https://github.com/lanefu/armlab/blob/bd56d6442588665e44eef210635ed07ee897b4b3/files/manifests/kubevirt/armbian/armbian_vm2_pvc_x86_64.yaml">my example</a>
uses an armbian/debian cloud-init image and attaches is to the bridge network</li>
<li>check your dhcp server and see if your VM is online.</li>
</ol>
<h3 id="useful-commands">useful commands</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl get dv
</span></span><span style="display:flex;"><span>kubectl get vmi
</span></span><span style="display:flex;"><span>kubectl get vm
</span></span></code></pre></div><p>It&rsquo;s nice to watch these assets from within k9s. remember if you do <code>esc :</code> in
k9s you can do manual queries for things like above.</p>
<p>Watching the cdi importer for creating the dv is great for troubleshooting.</p>
]]></content></item><item><title>A Debian Linux Router and Firewall with 2 weird names</title><link>https://blog.lane-fu.com/posts/2024/10/a-debian-linux-router-and-firewall-with-2-weird-names/</link><pubDate>Sun, 06 Oct 2024 09:51:54 -0400</pubDate><guid>https://blog.lane-fu.com/posts/2024/10/a-debian-linux-router-and-firewall-with-2-weird-names/</guid><description>&lt;p>I built an ansible-managed router and firewall solution called
&lt;a href="https://github.com/lanefu/clammy-ng/">clammy-ng&lt;/a> using only modern Linux
tooling&amp;hellip; and it&amp;rsquo;s pretty good! Did I mention it works on ARM?&lt;/p>
&lt;h2 id="craving-a-new-router-solution">Craving a new router solution&lt;/h2>
&lt;p>I was a Ubiquiti EdgeRouter fan for years. The series provided Amazing
capability for the price, and the OS being based on a fork of Vyatta made it all
that much more appealing.&lt;/p>
&lt;p>However, in recent years my opinion began to sour as it&amp;rsquo;s become evident that
priority on this product line is going way. Ubiquiti&amp;rsquo;s focus is clearly oriented
around their USG platform. I don&amp;rsquo;t blame them for it. It&amp;rsquo;s a better market
alignment for them. The EdgeRouter series isn&amp;rsquo;t a good fit for SMB&amp;rsquo;s and they
don&amp;rsquo;t provide enough support for enterprise.&lt;/p></description><content type="html"><![CDATA[<p>I built an ansible-managed router and firewall solution called
<a href="https://github.com/lanefu/clammy-ng/">clammy-ng</a> using only modern Linux
tooling&hellip; and it&rsquo;s pretty good! Did I mention it works on ARM?</p>
<h2 id="craving-a-new-router-solution">Craving a new router solution</h2>
<p>I was a Ubiquiti EdgeRouter fan for years. The series provided Amazing
capability for the price, and the OS being based on a fork of Vyatta made it all
that much more appealing.</p>
<p>However, in recent years my opinion began to sour as it&rsquo;s become evident that
priority on this product line is going way. Ubiquiti&rsquo;s focus is clearly oriented
around their USG platform. I don&rsquo;t blame them for it. It&rsquo;s a better market
alignment for them. The EdgeRouter series isn&rsquo;t a good fit for SMB&rsquo;s and they
don&rsquo;t provide enough support for enterprise.</p>
<p>Despite them being amazing capability for the price, they&rsquo;re still pretty pricey
and their CPUs are slow. Most performance relies on hardware offload
capabilities, so CPU oriented tasks like VPN solutions, and bonding can have an
impact. (Disclosure: LACP performance when I upgraded to an ER-6P was fine.) A
cheap ARM SBC like the
<a href="https://wiki.friendlyelec.com/wiki/index.php/NanoPi_R5S">NanoPi R5S</a> could
deliver the same performance for my use case.</p>
<p>My goal was retire my ER-6P and replace it with low-cost Armbian powered SBC.
The core requirements were simple:</p>
<ul>
<li>ansible managed</li>
<li>use mainline kernel</li>
<li>support zone a zone firewall configuration</li>
</ul>
<h2 id="approach">Approach</h2>
<h3 id="use-modern-components-when-possible">Use modern components when possible</h3>
<p>Basically I wanted to avoid getting bogged down in managing long orchestration
scripts whenever possible. When Vyatta/VyOS was made, we didn&rsquo;t have netplan,
networkd, frrouting, nftables, etc. Consequently, it has a ton of code to
directly manage interfaces, bird, and iptables. I wanted to offload those
burdens as much as possible.</p>
<p>Another part of off-loading those burdens, was to try to find as many
off-the-shelf ansible roles as possible to manage those modern services.
Shout-out to <a href="https://github.com/mrlesmithjr">mrlesmithjr</a> as he had flexible
roles for many of the components I needed.</p>
<h3 id="configuration">Configuration</h3>
<p>Since I&rsquo;m using ansible (the point of this project), this means managing the
configuration in a sane vars structure. The biggest part of this was creating a
structure that could describe most aspects of an interface and its networks in a
single place. I could then just reference components of that again later in the
configuration as needed.</p>
<p>I decided to use my
<a href="https://github.com/lanefu/clammy-ng/blob/fc1c7813d9a6f0b2b24a3db6bfed58884669df50/host_vars/nanopi-r5s.yml#L10">foomuuri_networks</a>
object as my main source of truth.</p>
<p>In the example snippets below you can see how I referenced other attributes of
the network later in the configuration</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">foomuuri_networks</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">test1</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">interface</span>: <span style="color:#ae81ff">test1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">description</span>: <span style="color:#ae81ff">test network 1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">zone</span>: <span style="color:#ae81ff">test1z</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#e6db74">&#34;192.168.101.1&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">netmask</span>: <span style="color:#e6db74">&#34;255.255.255.0&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address_summarized</span>: <span style="color:#e6db74">&#34;192.168.101.1/24&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">dhcp</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">start</span>: <span style="color:#ae81ff">192.168.101.100</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">end</span>: <span style="color:#ae81ff">192.168.101.150</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">lan_netplan_configuration</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">vlans</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">test1</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">id</span>: <span style="color:#ae81ff">1001</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">link</span>: <span style="color:#ae81ff">bond0</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">addresses</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#e6db74">&#34;{{ foomuuri_networks.test1.address_summarized }}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">dnsmasq_dhcp_scopes</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">interface</span>: <span style="color:#e6db74">&#34;{{ foomuuri_networks.test1.interface }}&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">netmask</span>: <span style="color:#e6db74">&#34;{{ foomuuri_networks.test1.netmask }}&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">start</span>: <span style="color:#e6db74">&#34;{{ foomuuri_networks.test1.dhcp.start }}&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">end</span>: <span style="color:#e6db74">&#34;{{ foomuuri_networks.test1.dhcp.end }}&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">lease_time</span>: <span style="color:#e6db74">&#34;4h&#34;</span>
</span></span></code></pre></div><h2 id="challenges">Challenges</h2>
<h3 id="the-firewall">The firewall</h3>
<p>Solving the firewall the <em>way</em> I wanted it to be solved was the biggest part of
the project.<br>
Most existing tools and scripts in the wild were interacting with iptables, and
sometimes even required legacy mode. I really wanted something strictly nftables
native if possible.</p>
<p>To further my pickiness&ndash;I also really really really wanted a zone firewall.</p>
<h4 id="firewalld">Firewalld</h4>
<p>I had originally assumed firewalld was going to be my path since it&rsquo;s a &ldquo;zone
firewall&rdquo; and has ansible modules to support it. The reality is, its a host
firewall and not a router firewall. To be a router firewall I need firewall
policies that are oriented around traffic traverse between 2 interfaces, rather
than traffic between an interface and the host. It is possible to do it with
firewalld, but it requires writing lots of custom rules in XML and managing
that. No thanks.</p>
<h4 id="foomuuri">Foomuuri</h4>
<p>Foomuuri is the salvation of this project. If you take away anything from this
blog, it&rsquo;s please <a href="https://github.com/FoobarOy/foomuuri/">check out Foomuuri</a>.
Timing wise I was really lucky. Foomuuri is quite new, originating around Feb
2023&hellip; and I happened to come across it a few months later where it had just
been added into debian unstable packaging. You don&rsquo;t need a debian package in
reality. Foomuuri is an extremely elegant python script with minimal
dependencies.</p>
<p>Foomuuri can function as a host firewall, and most importantly for this
project&hellip; It can function as a
<a href="https://github.com/FoobarOy/foomuuri/wiki/Router-Firewall">router firewall</a>.</p>
<p>Its configuration language is flexible, and easy to template. Like a good *nix
tool, the configuration files can be broken up and arranged in anyway the user
sees fit.</p>
<p>I made a
<a href="https://github.com/lanefu/ansible-collection-clammy/tree/main/roles/foomuuri">foomuuri ansible role</a>
for managing it&rsquo;s installation and configuration.</p>
<h2 id="the-code">The code</h2>
<p>It consists of the
<a href="https://github.com/lanefu/clammy-ng">clammy-ng ansible project</a> reference
implementation repo and the
<a href="https://github.com/lanefu/ansible-collection-clammy">clammy-ng ansible collection</a>
containing a filter plugin, roles such as my foomuuri role to implement the
solution.</p>
<h3 id="components-used">Components used</h3>
<p>My primary components right now include:</p>
<ul>
<li>dnsmasq</li>
<li>foomuuri</li>
<li>frrouting</li>
<li>inadyn</li>
<li>netplan</li>
<li>networkd</li>
<li>nftables</li>
<li>wireguard</li>
</ul>
<h3 id="check-it-out">Check it out!</h3>
<p>See the
<a href="https://github.com/lanefu/clammy-ng/tree/main#functionality-status">readme</a> for
demos, screenshots, and notes about other quality of life packages and scripts
provided.</p>
<h3 id="prs-welcome">PRs Welcome</h3>
<p>This was targeted for my use case, but I think it&rsquo;s a flexible start for others.
Feel free to fork or PR enhancements. Share your crazy configurations to inspire
others!</p>
]]></content></item><item><title>A LaneCloud Explanation</title><link>https://blog.lane-fu.com/posts/2024/07/a-lanecloud-explanation/</link><pubDate>Sat, 27 Jul 2024 10:31:24 -0400</pubDate><guid>https://blog.lane-fu.com/posts/2024/07/a-lanecloud-explanation/</guid><description>&lt;p>For those that have encountered me enough times &amp;ldquo;LaneCloud&amp;rdquo; will usually come up
with at least a passive mention. Some assume it&amp;rsquo;s just some kind of inside joke.
Some think it&amp;rsquo;s some sort of homelab. My favorite description comes from a
former colleague and friend who described it along the lines of &lt;em>&amp;ldquo;The strangest
experiment I&amp;rsquo;ve ever seen.&amp;rdquo;&lt;/em>&lt;/p>
&lt;p>There&amp;rsquo;s some truth in all those conclusions. To be clear, the official tagline
is:&lt;/p></description><content type="html"><![CDATA[<p>For those that have encountered me enough times &ldquo;LaneCloud&rdquo; will usually come up
with at least a passive mention. Some assume it&rsquo;s just some kind of inside joke.
Some think it&rsquo;s some sort of homelab. My favorite description comes from a
former colleague and friend who described it along the lines of <em>&ldquo;The strangest
experiment I&rsquo;ve ever seen.&rdquo;</em></p>
<p>There&rsquo;s some truth in all those conclusions. To be clear, the official tagline
is:</p>
<blockquote>
<p>The premiere not-for-hire cloud provider serving niche tech communities and
driving innovation</p></blockquote>
<h2 id="organic-origins">Organic Origins</h2>
<p>My participation in the <a href="https://armbian.com">Armbian</a> project led me down the
path of placing a 40 thread server in my rack at home. This REALLY sped up
kernel compilation&ndash;which is something you do constantly if your hobby is
oriented around the <a href="https://github.com/armbian/build">Armbian Build Framework</a>
to make Linux images for weird SBCs.</p>
<p>I built the server to be pretty flexible. It could run docker natively as well
as run VMs with libvirt. Although that sounds trivial, it&rsquo;s actually quite a
pain, as docker is really aggressive in inserting itself at the end of the
firewall chain. Custom rules have to be added for network traffic to flow
properly on other bridge interfaces.</p>
<h2 id="serving-others">Serving Others</h2>
<p>Is it <em>really</em> a server if there&rsquo;s just one user? Obviously I&rsquo;m not compiling
kernels all day long, so it was very reasonable to want to give more people
access to the speed I had. I provided a few very large build VMs to some people
in the ARM SBC community so they could enjoy the same time-saving performance I
did when building kernels. Additionally, a few GitHub Actions runners for
Armbian were also added to share the pipeline load.</p>
<p>Late one afternoon a co-worker pinged me. They were working on an internal
project, and their GitLab CI pipeline was giving weird errors. <em>IT WAS DNS</em> and
our European counterparts that maintain those runners would be unavailable until
the next business day. There&rsquo;s nothing worse than being in the flow and then
being obstructed by somebody else&rsquo;s IT problem.</p>
<p>Never to leave a comrade behind when they&rsquo;re down, I took action. I configured a
Gitlab Runner agent with DIND on my big iron, registered it to our GitLab
server, added some labels, and assigned it to his project.</p>
<p>&ldquo;Try Now.&rdquo;</p>
<p>Productivity resumed&hellip;and all those threads really made the pipeline much
faster. We kept the runner bound to the project indefinitely.</p>
<h2 id="increasing-criticality">Increasing Criticality</h2>
<p>True to Cloud TCO rhetoric, you <em>will</em> occasionally encounter hardware problems
when running your own hardware. My experience is no exception. My OS drive was a
first gen SSD which had been fine, but as this server&rsquo;s responsibilities had
increased, something was occasionally causing the SSD firmware to glitch and
cause the server to misbehave.</p>
<p>Glitches like that are quite insightful. They remind you that your are providing
services that other people rely on. The DM&rsquo;s come fast. &ldquo;I can&rsquo;t reach my VM.&rdquo;
&ldquo;Is the server down?&rdquo; &ldquo;The CI Runner is hung.&rdquo; &ldquo;There&rsquo;s something wrong with
your network.&rdquo;</p>
<p>After one such issue resolution I jokingly replied: &ldquo;The issue has been
resolved. Sorry for the inconvenience. Thank you for your patience and thanks
for choosing LaneCloud.&rdquo;</p>
<h2 id="black-friday-deals">Black Friday Deals</h2>
<p>It doesn&rsquo;t take a QuinnyPig hot-take to figure out that some customers don&rsquo;t
need the level of service and associated costs that come with using a top-tier
hyperscaler. You&rsquo;re likely aware of the next tier down&ndash;Linode, Digital Ocean,
Vultr, Hetzner, etc. &hellip;but you can keep going down and start to find specialty
hosting companies, and indie operations.</p>
<p>Maybe you&rsquo;ve heard of <a href="https://lowendbox.com">LowEndBox</a>, a fairly organized
index of low-cost hosting options. Now we&rsquo;re getting close with LowEndBox to
what we&rsquo;re seeking. We&rsquo;ve left the business district and gone to the commercial
district, but still entering through the store-front.</p>
<p>It&rsquo;s time to go to the flea market. The <a href="https://lowendtalk.com">LowEndTalk</a>
forums are the definitive flea market for cloud services, colocation, and
hosting. Like a true flea market, there&rsquo;s strange etiquette, questionable
vendors, reputable vendors, amazing deals, haggling, and plenty of buyer-be-ware
scenarios. There&rsquo;s a lot of overlap between LowEndBox and LowEndTalk vendors,
but the LowEndTalk community gives you a lot of perspective&ndash;including realizing
there&rsquo;s plenty of organizations still happily running LAMP stacks and simple
services.</p>
<p>Between Black Friday and March Madness deals where you pre-pay for 1 or 2 years
of hosting for pennies on the dollar, I found myself with an inventory at least
10 VPS instances distributed across the globe. The compute capacity of LaneCloud
was expanding.</p>
<h2 id="branding-spiral">Branding Spiral</h2>
<p>By this point, the &ldquo;LaneCloud&rdquo; references were common Slack jokes, and we&rsquo;re in
peak covid lockdown so I decided it was time to seek out a professional and
obtain an official Logo. Fiverr.com to the rescue.</p>
<p>I found an artist that specialized in minimalist 2d logos and enlisted their
services. They gave me 4 choices&hellip; I selected 2 of them. One which I thought
was more practical and I consider to be the official logo and another one that
was a little more retro and technical which I opted to keep as an unofficial
logo for backend resources or testing.</p>
<p>&ldquo;Would you also like the social media kit?&rdquo; &hellip;uh sure.</p>
<p>The results were complete and definitely worth the modest rate the artist
charged. I had PNGS, SVGS, EPS as well as Adobe Illustrator original files. The
social media kit educated me on the existence of custom headers and footers you
can apply to LinkedIn, Xitter, and Facebook. I started feathering some of these
new graphics into my profiles.</p>
<p>I had such a good experience, I wanted to see <em>what else</em> I could get on
Fiverr.<br>
Well, If you write your own script&ndash;with a little bit of luck you can hire a
professional actor for $60 and get it back and 4 hours.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/ptseUMG8VKY?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="LaneCloud Commercial"></iframe>
    </div>

<p>Since I had invested in branding and marketing, it felt prudent to commit to the
name and purchase some domains. I secured
<a href="https://lanecloud.cloud">lanecloud.cloud</a> as my official public-facing domain.
The sharks at afternic, GoDaddy&rsquo;s domain squatting division, rejected my
generous $200 counter-offer for <code>lanecloud.com</code>. They periodically send
insulting emails to see if that will motivate me to buy.</p>
<blockquote>
<p>Hi Lane,</p>
<p>Have you given up on this project? Is it safe to assume that you are no longer
interested?</p>
<p>Let me know if you would like to make an offer.</p></blockquote>
<p>I always make the same offer and they don&rsquo;t respond.</p>
<p>Being <code>lanecloud.com</code>-less and having $200 remaining in my &ldquo;marketing budget,&rdquo; I
clearly needed to solve this problem with swag.
<a href="https://stickermule.com">StickerMule</a> is absolutely amazing. Their UI is easy,
and their design team responds back quickly with proofs. They&rsquo;ll gladly make
kerning adjustments and other reasonable modifications without additional fees.
Armed with die cut LaneCloud stickers, static clings, and even branded packing
tape, it was time to define a product.</p>
<h2 id="product-vision-attempt">Product Vision Attempt</h2>
<p>I found myself with a problem in search of a problem. I had all these cheap VPSs
doing nothing&ndash;&ldquo;Idlers&rdquo; in LowEndTalk parlance. Servers with nobody to serve is
a sad problem.</p>
<p>I concluded that meshing my pool of random VPS nodes into a unified
<a href="https://nomadproject.io">Nomad Cluster</a> would be a great way to leverage the
resources. That solved my first problem. The answer to my second problem was
revealed through the process of writing my fiverr commercial.</p>
<blockquote>
<p>Pilot your new applications in LaneCloud</p></blockquote>
<p>My general idea evolved into this basic thinking:</p>
<p>As an SRE-oriented person, I felt like it was still tragically too common that
indie developers are still spinning up VPS instances and manually operating
their apps for POCs and pilots. IMHO this means they&rsquo;re missing out on a good
metrics and alerting solution, good log aggregation and dashboard solution, and
a sane solutions for secrets management. I felt like I could package up an
opinionated solution that made sure developers had these things challenges
solved&hellip;and strongly encouraged them to use it.</p>
<p>Clearly, this was somewhat inspired by <a href="https://www.heroku.com">Heroku</a>, but I
wanted this to be more container oriented&ndash;perhaps even exposing some aspects of
Nomad to the users as well. The boundaries of LaneCloud would be crisp.
Ephemeral service deployment, with monitoring, logging, and secrets management
provided. For any other persistent storage, the customer would need to use a
cloud solution such as object storage or a managed database.</p>
<p>The purpose of LaneCloud was to provide a better starting point for developers
that want to build an app and take it through a pilot stage. Besides the
observability and secrets features, LaneCloud would be able to provide moderate
scaling across it&rsquo;s pool of inexpensive compute resources. This allows a project
to grow organically for a longer period of time, before needing to shift to a
higher SLA cloud provider, or needing to rush and refactor their stack away from
a single ec2 or VPS instance.</p>
<p>Following the same formula, I realized in addition to developers, LaneCloud
could help hobbyist run popular &ldquo;self-hosted&rdquo; genre of applications such as the
ones packaged on <a href="https://linuxserver.io">linuxserver.io</a>. Having two viable use
cases with the same base solution gave me confidence and a clear-enough vision
that I could start planning.</p>
<h2 id="architecture-brainstorm">Architecture Brainstorm</h2>
<p>I started sketching down all the core infrastructure needs and tasks I&rsquo;d
ultimately need to tackle. It consumed several pages in my field notes and
decided it was worth hanging onto in a more legible medium. I typed it up in a
document as a basic &ldquo;How to build a cloud&rdquo; framework.</p>
<p>In reality, it&rsquo;s just things you need to consider for any &ldquo;production&rdquo;
greenfield environment. Even with managed services, these things still matter.</p>
<p>Here&rsquo;s the raw headings from the outline:</p>
<h3 id="my-how-to-build-a-cloud-rough-outline">My &ldquo;how to build a cloud&rdquo; rough outline</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#75715e">## Deploy an Inventory IPAM tool
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Define a basic taxonomy
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Make an out-of-band control Plane
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Build Monitoring Stack
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Internal Network
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Documentation and Version Control System
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Choose a secrets manager
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Solve for DNS
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Plan for User Management and Access Control
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Pick a task scheduler
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Persistent Storage Plan
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Worker Instances
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Service Discovery
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Ingress Control
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Edge Security
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## Artifact Management
</span></span></span></code></pre></div><h2 id="home-base-required">Home Base Required</h2>
<p>Although the core model behind LaneCloud was distributed low-cost VPS instances,
I intended to treat them like spot instances and assume a lack of reliability.
I&rsquo;d need a reliable hub to operate my nomad control plane, coordinate mesh
networking, etc.</p>
<p>As this central location idea was brewing, I had summer with several power
outages. On several occasions I found myself sprinting to plug in the generator
before my UPS ran out. I was seldom fast enough.</p>
<p>Familiar messages would appear: &ldquo;I can&rsquo;t reach my VM.&rdquo; &ldquo;Is the server down?&rdquo;
&ldquo;The CI Runner is hung.&rdquo;</p>
<p>Signs were pointing me into a single direction. It was becoming evident that my
server needed to go into colocation.</p>
<h2 id="texas-hospitality">Texas Hospitality</h2>
<p>I got a few quotes locally, but they weren&rsquo;t quite in the price range I was
looking for.<br>
My search expanded to posting on LowEndTalk for more hosting options in my area.
Not to many leads, but Josh from <a href="https://dataideas.com">Data Ideas</a> replied to
my post and invited me to &ldquo;come down to Texas.&rdquo;</p>
<p>I hadn&rsquo;t considered Texas. I&rsquo;m spoiled with my sub 10ms ping times from Richmond
to Ashburn&ndash;how could I <em>ever</em> consider something 40ms away? I began considering
that I would live and popped onto the Data Ideas Discord server to learn more
about this small provider.</p>
<p>Data Ideas is a small business that specializes in providing Colocation, VPS,
and even RPI hosting to tech-enthusiasts like me. This means they&rsquo;re much more
willing to take a customer like me seriously, help get my equipment online, deal
with any irregular non-enterprise hardware I might have, and just generally be
very accommodating. The pricing was also fantastic.</p>
<p>I began working out an arrangement to send 2 servers, a switch, a router and an
<a href="https://ameridroid.com/products/odroid-hc4">Odroid HC4</a>. Sending an ARM powered
driver-toaster is a great example of that irregular hardware case.</p>
<p>Before finalizing my arrangements, an existing customer on the Data Ideas
Discord pinged me. He was wanting to expand his footprint and had a proposal for
me to split 10U, <em>a lot of network bandwidth</em> and a generous amount of IPv4
addresses. I probed a little more to make sure I wasn&rsquo;t setting myself up for
trouble. The guy wasn&rsquo;t doing anything nefarious with his half the resources,
was very talented at BGP routing, and generally was a good match.</p>
<p>That&rsquo;s how I met my rackmate, and how I also invented the word <em>rackmate</em>.</p>
<p>I started boxing up my iron, and thanks to
<a href="https://pirateship.com">Pirate Ship</a>, LaneCloud went to Texas.</p>

    <img src="/images/lanecloud_explanation/lanecloud_server_boxed.png"  alt="large box in truck bed sealed with lanecloud branded tape"  class="center"  style="border-radius: 8px;"  />



    <img src="/images/lanecloud_explanation/lanecloud_racked.png"  alt="combined server resources in rack"  class="center"  style="border-radius: 8px;"  />


<h2 id="control-plane-v1">Control Plane V1</h2>
<p>Although the long-term vision was certainly containers, I felt it was important
to be able to provision VMs gracefully as a first step. My need was legitimate,
but I was partially inspired by the fact that many VPS providers in the
LowEndTalk space were using primitive solutions. I wanted to demonstrate a
&ldquo;simplified&rdquo; approach using modern tooling.</p>
<p>The provisioning tooling for <em>LaneCloud Alpha</em> could easily be it&rsquo;s own blog
post. I&rsquo;ll just enumerate some highlights:</p>
<ul>
<li>automation is driven by <a href="https://ansible.com">Ansible</a></li>
<li>IP is assigned from <a href="https://github.com/netbox-community/netbox">Netbox</a></li>
<li>VM scheduled by
<a href="https://developer.hashicorp.com/nomad/docs/drivers/qemu">Nomad&rsquo;s qemu driver</a></li>
<li>custom cloud-init config generated by Ansible and Nomad template</li>
<li>additional user customization available as a
<a href="https://cloud-init.org">cloud-init</a> include stored in consul</li>
<li>Public DNS entry updated via Ansible module for
<a href="https://pypi.org/project/dns-lexicon/">dns-lexicon</a></li>
<li>VM fully provisioned and available on internet with public IP in under 2
minutes.</li>
</ul>
<p>Check out my
<a href="https://github.com/lanefu/lanecloud-snippets">LaneCloud snippets repo</a> for more
details.</p>
<h2 id="re-thinking-objectives">Re-thinking Objectives</h2>
<p>As time has progressed, my intentions around LaneCloud have changed a few times.</p>
<p>The lack of native ARM runners with GitHub Actions (now resolved) inspired me to
pursue ARM bare-metal hosting using Raxda Rock 5B boards. Sadly I underestimated
how long until the
<a href="https://www.cnx-software.com/2020/02/01/rockchip-rk3566-rk3588-rv1109-socs-coming-in-2020-based-on-rockchip-processor-roadmap/#rockchip-rk3588">RK3588</a>
would actually become viable to reliable run VM&rsquo;s and containers. I should
certainly know better by now.</p>
<p>Fast forward 2 years and the RK3588 is generally viable for such a use, but as
time progressed I realized this wasn&rsquo;t a path I wanted to go down. Low-cost
hosting is a race to the bottom. Observing all the dialog on LowEndTalk made me
realize that public customers in this space are brutal, prone to fraud, and
expect a lot for a little. This was not the direction I wanted to go.</p>
<p>I&rsquo;ve also began reconsidering my entire nomad-based architecture. It&rsquo;s clever,
but really isn&rsquo;t made for persistent VMs. It&rsquo;s difficult to manage console
connections and maintain state. Between the
<a href="https://www.theregister.com/2023/08/11/hashicorp_bsl_licence/">recent HashiCorp License Changes</a>
and Kubernetes tooling at critical mass adoption, I&rsquo;m ready to switch to
Kubernetes and also use KubeVirt for any VM needs. Maybe one-day I&rsquo;ll even get
to MicroVMs.</p>
<h2 id="what-is-probably-next">What is probably next</h2>
<p>As far as getting LaneCloud into the marketplace, I&rsquo;m not sure I&rsquo;m really going
to pursue anything directly right now. It&rsquo;s probably best at doing what it
already does, which is serve the needs of myself, friends, and some of the tech
community. I like the idea a &ldquo;core services&rdquo; platform-engineering style offering
that I could make available to some folks. Perhaps operating as a co-op to let a
small group folks take advantage of pre-configured tooling and resources is the
most practical path.</p>
<p>Regarding the architecture, it&rsquo;s definitely time to replace my Hashistack
components with Kubernetes. As previously mentioned, it&rsquo;s reached a state where
I&rsquo;m sold on it. The operator model is great, and there&rsquo;s plenty of ways for me
to easily integrate things. I really hope I can make it to MicroVMs.</p>
<h2 id="so-what-is-lanecloud-really">So what is LaneCloud Really</h2>
<p>Mainly LaneCloud is a way to keep me grounded in technology and remind me what
it means to run things in the wild. With my $dayjob in consulting, I operate in
a lot of spaces, but don&rsquo;t always get to observe them long term. It helps me
maintain several perspectives, including why most organizations should just use
the cloud instead of running their own stuff.</p>
<p>I do enjoy having all the resource capability at a fixed cost. That&rsquo;s not a
financial argument tho, LaneCloud is definitely still a loss-leader.</p>
<p>Many may still just view it as a glorified homelab that escaped the lab, but I
much prefer to view it as <em>the strangest experiment my friend has ever seen.</em></p>
]]></content></item><item><title>Token First Post</title><link>https://blog.lane-fu.com/posts/2024/02/token-first-post/</link><pubDate>Sat, 24 Feb 2024 10:58:06 -0500</pubDate><guid>https://blog.lane-fu.com/posts/2024/02/token-first-post/</guid><description>&lt;p>It&amp;rsquo;s really weird to come full circle and come back to just wanting simple static content to post on a web-page.&lt;/p>
&lt;h2 id="digging-slightly-deeper">Digging slightly deeper&lt;/h2>
&lt;p>I got addicted to just sharing my projects/thoughts etc in real-time over &amp;ldquo;big chat&amp;rdquo; (just made that up), and social media.
As we know&amp;ndash;social media takes more than it gives these days. Sharing on chat is fun, but even that can be information
overload for the friends you&amp;rsquo;re trying to share with&amp;hellip; Especially if half of its really notes.
I&amp;rsquo;m very prone to stream-of-conciousness types of messaging. I&amp;rsquo;m sure its fatiguing for others.&lt;/p></description><content type="html"><![CDATA[<p>It&rsquo;s really weird to come full circle and come back to just wanting simple static content to post on a web-page.</p>
<h2 id="digging-slightly-deeper">Digging slightly deeper</h2>
<p>I got addicted to just sharing my projects/thoughts etc in real-time over &ldquo;big chat&rdquo; (just made that up), and social media.
As we know&ndash;social media takes more than it gives these days.   Sharing on chat is fun, but even that can be information
overload for the friends you&rsquo;re trying to share with&hellip; Especially if half of its really notes.
I&rsquo;m very prone to stream-of-conciousness types of messaging.  I&rsquo;m sure its fatiguing for others.</p>
<h3 id="beyond-note-taking">beyond note taking</h3>
<p>Hedgedoc has been a blessing for keeping notes in markdown.  I really love it.. but I don&rsquo;t like it much as a medium
for sharing, even though it has a concept of publishing.   Needs more decoupling.  Also it&rsquo;s theme is sometimes too
narrow of a fixed with for snippets.</p>
<h3 id="the-right-filing-system">the right filing system</h3>
<p>Lately I&rsquo;ve been reaaally craving a blog to share my more recent science projects.. It&rsquo;s been eating at me.. so that&rsquo;s a sign.
I have to say even going through this exercise of writing this first-post that it&rsquo;s therapeutic to know I&rsquo;m filing this
content in an appropriate location.</p>
<h2 id="the-eternal-turmoil-of-momentum-vs-principled-computing">The Eternal turmoil of momentum vs principled computing</h2>
<p>&ldquo;Just setup hugo to deploy in github pages&rdquo; is the 2024 equivalent of &ldquo;just scp files to an nginx server you manually configured&rdquo;.</p>
<p>Naturally if I&rsquo;d done either of those things, this would have been online 10 months ago&hellip; but I can&rsquo;t.  I&rsquo;d been trying to hold out
until I have a public-facing k8s tooled correctly with external-dns and cert-manager, but my compromise for now is to just deploy
onto lanecloud object storage and front-end with an nginx i already have deployed and managed via ansible.</p>
<h2 id="testing-code-snippets">testing code snippets</h2>
<h3 id="yaml">yaml</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Alice</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">age</span>: <span style="color:#ae81ff">30</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">city</span>: <span style="color:#ae81ff">New York</span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Bob</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">age</span>: <span style="color:#ae81ff">25</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">city</span>: <span style="color:#ae81ff">London</span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Charlie</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">age</span>: <span style="color:#ae81ff">32</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">city</span>: <span style="color:#ae81ff">Paris</span>
</span></span></code></pre></div><h3 id="bash">bash</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export HELLO<span style="color:#f92672">=</span>world
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#e6db74">${</span>VAR<span style="color:#e6db74">}</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>SOMETHING<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>  printf<span style="color:#f92672">(</span><span style="color:#e6db74">&#34;done\n&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span></code></pre></div><h3 id="python">python</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>message <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Hello, World!
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">This is a multi-line string
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">with multiple lines and spaces.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(message)
</span></span></code></pre></div><h3 id="golang">golang</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Hello, World!&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content></item></channel></rss>