<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on The Platfrastructure Life</title>
    <link>https://platfrastructure.life/post/</link>
    <description>Recent content in Posts on The Platfrastructure Life</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>platfrastructure@xy0.org (Ted Wood)</managingEditor>
    <webMaster>platfrastructure@xy0.org (Ted Wood)</webMaster>
    <copyright>&amp;copy; 2016 -- 2026. All rights reserved.</copyright>
    <lastBuildDate>Wed, 24 Jun 2026 08:00:09 -0400</lastBuildDate>
    
        <atom:link href="https://platfrastructure.life/post/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>What do I use?</title>
      <link>https://platfrastructure.life/post/2026-06-24/</link>
      <pubDate>Wed, 24 Jun 2026 08:00:09 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/2026-06-24/</guid>
      <description>&lt;p&gt;Inspired by &lt;a href=&#34;https://kevquirk.com/uses&#34;&gt;Kevin Quirk&lt;/a&gt; I have decided to make one
of those &lt;a href=&#34;https://platfrastructure.life/uses&#34;&gt;Uses pages&lt;/a&gt; in which I try to
catalog the things I use.&lt;/p&gt;
&lt;p&gt;If you read it and have thoughts or comments, feel free to drop me a line
&lt;a href=&#34;mailto:blogreplies@xy0.org?subject=What%20do%20I%25use%3F&#34;&gt;via email&lt;/a&gt;. I am
always interested to hear what my readers have to say.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>New Beginnings</title>
      <link>https://platfrastructure.life/post/new_beginnings/</link>
      <pubDate>Thu, 22 Jan 2026 12:40:00 +0000</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/new_beginnings/</guid>
      <description>&lt;p&gt;Happy Thursday and welcome to another day in my life. This is a bittersweet
post today; I am changing jobs. In 2016 I accepted an offer to join the System
Engineering team with Mailchimp. The team at the time was relatively small,
maybe 20-30 engineers within operations at the time. The company was hiring
and growing very rapidly during this period. Within 6 months of being hired I
was one of the more senior engineers on the team. A few years later, around
2019, I was promoted to Senior Engineer and changed teams, internally to work
with our internal dev tooling team. This team would later become what modern
organizations would consider developer experience. My day-to-day shifted from
break fixes and standard operations type work to more development and infra
tooling work.&lt;/p&gt;
&lt;p&gt;Shortly after this transition the world was tossed upside down with COVID and
the associated lock-downs. The office closed and everyone began to work from
home. This was fine for my team at the time because we were already one of the
few mostly remote teams already. The rest of the company, however, struggled
to adapt.&lt;/p&gt;
&lt;p&gt;Fast-forward to 2021 and it&amp;rsquo;s announced that Mailchimp is to be acquired by
Intuit. There was a lot of concern among the general rank and file that this
was going to lead to the destruction of our carefully curated culture. Many
people left during this time and things were generally a little chaotic. But,
for the next couple of years, things hummed along with business as usual. That
was until July 2024.&lt;/p&gt;
&lt;p&gt;In July 2024, Intuit announced that they would be doing a fairly sizeable
layoff; reducing their staff by about 10%. This sent a shock through the
Mailchimp employees as it was seen by many that the layoff disproportionately
affected Mailchimp&amp;rsquo;s numbers. This was purely speculation and I won&amp;rsquo;t comment
on it directly but to say that it was seen as a change in posture within the
company and not one that many long-time Mailchimp employees liked. In addition
to the layoffs, many people voluntarily left the company in the following days
and weeks as a result of their shift in feelings towards the new parent
company.&lt;/p&gt;
&lt;p&gt;For the next two years, things continued along a new trajectory. Intuit, being
fully invested in the AWS ecosystem, was pushing hard on Mailchimp engineering
to consolidate our services in to their environment. Most of the work being
done shifted from new features to lift-and-shift migrations and
re-architecting of core services. After a while, the writing began to appear
on the wall that those of us who worked on what was now seen as the &amp;ldquo;legacy&amp;rdquo;
Mailchimp environments were going to be out of a job in short order. It was
time to start figuring out what to do next.&lt;/p&gt;
&lt;p&gt;And that leads me to today, January 22, 2026. After 10 years with
Mailchimp/Intuit I have tendered my resignation. I leave the company with no
ill will and wish the best to those I&amp;rsquo;ve worked with over the years who will
remain. The company is shifting as all companies do and some will have to find
new opportunities. I hope that many of them are able to find internal mobility
moves when the time comes but I recognize that some of them will have to find
new opportunities outside the company. This is just a reality of business and
I wish everyone the best.&lt;/p&gt;
&lt;p&gt;I look forward to starting the next chapter in my story. I&amp;rsquo;ve accepted an SRE
position with &lt;a href=&#34;https://censys.com/&#34;&gt;Censys&lt;/a&gt;. It seems like a really good fit
in terms of matching up my existing skills to their needs while also opening a
lot of doors to grow in areas that I would have struggled to do within Intuit.
I&amp;rsquo;m excited to get to know the new team and get the opportunity to finally
work remotely, full-time.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>VIM-like navigation in MacOS</title>
      <link>https://platfrastructure.life/post/macos_vim_navigation/</link>
      <pubDate>Thu, 20 Nov 2025 09:46:34 -0500</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/macos_vim_navigation/</guid>
      <description>&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I have been a heavy VIM user (and now Neovim user) for many, many years now.
It&amp;rsquo;s to the point where the vim style of navigation and motions are deeply
ingrained in my brain and muscle memory. So, naturally, I want to leverage this
super power in all parts of my computer experience. This has lead me on a long
journey to continually find ways to adapt every aspect of my setup to support
at least some semblance of VIM-like feel.&lt;/p&gt;
&lt;p&gt;I use a variety of operation systems from MacOS to Linux (I use Arch, btw) to,
even still, Windows. Some of these operation systems make it easier than others
to accommodate my preferences. Others, such as MacOS (the subject of this
post), not so much.&lt;/p&gt;
&lt;p&gt;This post will focus on this difficult category of MacOS interface
customization. Over the years I have sourced a number of blogs and
StackOverflow posts to inform my setup but, most recently I stumbled upon &lt;a href=&#34;https://heywoodlh.io/mouseless-workflows-on-macos&#34;&gt;this
post&lt;/a&gt; by Mastodon user
&lt;a href=&#34;https://mastodon.social/@heywoodlh&#34;&gt;heywoodlh&lt;/a&gt;. Many of the topics discussed
herein are derived from this post so kudos to them for the inspiration.&lt;/p&gt;
&lt;h1 id=&#34;key-components&#34;&gt;Key Components&lt;/h1&gt;
&lt;p&gt;To make this all work, we&amp;rsquo;re going to need a handful of applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://vimium.github.io/&#34;&gt;Vimium&lt;/a&gt; - Keyboard navigation for your browser&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://shortcat.app/&#34;&gt;Shortcat&lt;/a&gt; - Keyboard navigation for your OS&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/BlueM/cliclick&#34;&gt;cliclick&lt;/a&gt; - CLI driven mouse actions&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.hammerspoon.org/&#34;&gt;hammerspoon&lt;/a&gt; - Automation tool for MacOS&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/koekeishiya/skhd&#34;&gt;skhd&lt;/a&gt; - Custom shortcuts (used here for
calling cliclick with keyboard shortcuts)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;vimium&#34;&gt;Vimium&lt;/h1&gt;
&lt;p&gt;Vimium provides a way to navigate webpages within your browser using a very
vim-native feel. Some of the key features of this plugin that I use every day
are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hjkl page navigation&lt;/li&gt;
&lt;li&gt;link highlighting&lt;/li&gt;
&lt;li&gt;URL yanking&lt;/li&gt;
&lt;li&gt;searching a page with /, n and N&lt;/li&gt;
&lt;li&gt;gg and G to jump to the top and bottom of a page&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are just some examples of what you can do with the plugin. Be sure to
visit the app page linked above for all the different commands that can be
used. Additionally, Vimium provides a powerful filtering option to allow you to
exclude certain webpages from vim navigation. This is particularly helpful for
websites, such as Gmail, that have their own keyboard shortcuts that may
conflict or for pages that do not behave as you expect with the plugin.&lt;/p&gt;
&lt;h1 id=&#34;shortcat&#34;&gt;Shortcat&lt;/h1&gt;
&lt;p&gt;The article by heywoodlh, referenced above, discusses using a program called
Vimac to generate overlays of keyboard-selectable &amp;ldquo;buttons&amp;rdquo; within the focused
application. Unfortunately, in the intervening years since the post was
written, this product has been deprecated and development work has moved to a
paid application called Homerow. While I&amp;rsquo;m sure that Homerow is a fantastic
product, and I did try it out briefly, it is a paid application and Shortcat is
free (as in beer) and MacOS is my work environment on to which I do not wish to
invest money of my own in. Homerow has some features that are missing from
Shortcat, such as keyboard driven scrolling, so I would encourage you to check
it out if that is appealing to you.&lt;/p&gt;
&lt;p&gt;Shortcat provides the same keyboard-selectable overlay that is provided on a
webpage by Vimium but does so using the accessibility features of MacOS to
provide them within the focused application window. This can be very handy for
navigating applications that do not have their own shortcuts or have shortcuts
that are unintuitive and can&amp;rsquo;t be changed (I&amp;rsquo;m looking at you Slack).&lt;/p&gt;
&lt;h1 id=&#34;cliclick&#34;&gt;cliclick&lt;/h1&gt;
&lt;p&gt;Cliclick is a new piece of software to me. It is a CLI tool that allows the
execution of mouse and keyboard related actions from the terminal. With this
tool, you can fill in any of the gaps in shortcut-based navigation provided by
the previous two applications by directly moving and clicking the mouse through
the command line. However, doing this through the terminal is quite
inconvenient and we need some way to do it with a keyboard shortcut to really
tie it all up with a nice bow.&lt;/p&gt;
&lt;h1 id=&#34;hammerspoon&#34;&gt;Hammerspoon&lt;/h1&gt;
&lt;p&gt;Unfortunately, Cliclick does not support scroll wheel activation. It looks
like there is a Github &lt;a href=&#34;https://github.com/BlueM/cliclick/issues/10&#34;&gt;issue&lt;/a&gt; for
this but it has been open for years with no resolution. This is where
Hammerspoon comes in. Hammerspoon is a tool for automating tasks on MacOS. It
is configured with a Lua script that can be used to execute arbitrary commands
and actions. We will use this to scroll the window when we need to. There may
even be way to replace cliclick with Hammerspoon entirely but I have not
investigated that yet.&lt;/p&gt;
&lt;h1 id=&#34;skhd&#34;&gt;skhd&lt;/h1&gt;
&lt;p&gt;This handy application is the real gem of this entire setup in my opinion and
is the key pieces that I took away from the post by heywoodlh. skhd is a
generic keyboard shortcut application for MacOS that is configured with a
config file ($HOME/.skhd). Using skhd we can execute our cliclick commands from
keyboard shortcuts. Heywoodlh provides an excellent configuration example that
I&amp;rsquo;ve directly copied and used in my own configuration and will share here:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ctrl - k : cliclick &amp;#34;m:+0,-20&amp;#34; #up
ctrl - j : cliclick &amp;#34;m:+0,+20&amp;#34; #down
ctrl - l : cliclick &amp;#34;m:+20,+0&amp;#34; #right
ctrl - h : cliclick &amp;#34;m:-20,+0&amp;#34; #left

ctrl + shift - k : cliclick &amp;#34;m:+0,-40&amp;#34; #up (faster)
ctrl + shift - j : cliclick &amp;#34;m:+0,+40&amp;#34; #down (faster)
ctrl + shift - l : cliclick &amp;#34;m:+40,+0&amp;#34; #right (faster)
ctrl + shift - h : cliclick &amp;#34;m:-40,+0&amp;#34; #left (faster)

ctrl - 0x21 : cliclick ku:ctrl c:. #click with ctrl+[
ctrl - 0x1E : cliclick ku:ctrl rc:.  #right click with ctrl+]

ctrl + shift - 0x21 : /opt/homebrew/bin/hs -c &amp;#39;scroll.scrollUp()&amp;#39; #scroll up with ctrl+shift+[
ctrl + shift - 0x1E : /opt/homebrew/bin/hs -c &amp;#39;scroll.scrollDown()&amp;#39; #scroll down with ctrl+shift+]
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;That&amp;rsquo;s it for now. That&amp;rsquo;s my basic setup. There are a few other small changes
I&amp;rsquo;ve made to the MacOS keyboard defaults such as swapping capslock with escape,
for easier access to the escape key from the homerow, but that&amp;rsquo;s about it.&lt;/p&gt;
&lt;p&gt;I hope you find the information above useful and if you have other suggestions
on ways to improve this setup or want to share your own setups that help
improve your keyboard driven experience on MacOS, please &lt;a href=&#34;mailto:blog%40xy0.org?subject=MacOS%20Keyboard%20Navigation&#34;&gt;send me an
email&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Meshtastic Hardware Overview</title>
      <link>https://platfrastructure.life/post/meshtastic-hardware-thoughts/</link>
      <pubDate>Tue, 18 Nov 2025 07:32:32 -0500</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/meshtastic-hardware-thoughts/</guid>
      <description>&lt;p&gt;I get asked fairly frequently what sort of hardware I recommend for someone
just getting started with Meshtastic. I get asked often enough that I&amp;rsquo;ve
decided I should probably write this down to make it easy to share and to make
the information a bit more discoverable.&lt;/p&gt;
&lt;h1 id=&#34;a-question&#34;&gt;A Question&lt;/h1&gt;
&lt;p&gt;The question I get is usually something along the lines of the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I am new to this whole meshtastic thing but I want to buy a radio or two to
get started. What do you recommend for me?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first answer to this question is actually another question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What is your use case?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a question simple but within it hides a lot of nuance. That nuance is
what I&amp;rsquo;m looking for. Does the person want to carry the node in their bag or
pocket? Do they want to install it in a permanent location? Maybe they want to
put it on the roof of their car. Do they have access to grid power or will they
want to run it on solar? All of these are important things to consider when
choosing the right radio.&lt;/p&gt;
&lt;h1 id=&#34;portable-use&#34;&gt;Portable Use&lt;/h1&gt;
&lt;p&gt;In this first of our Radio Recommendation series, we will cover portable use
cases. I am going to cover this first as it is often the most common,
especially for someone just getting started in the hobby. Portable radios
should focus on two key aspects: size and battery life. In the following
sections I&amp;rsquo;m going to discuss specific radios that I believe fit this use
case. I will discuss what they are and hit on a few key pros and cons of each
device. As with all battery powered devices, runtime will vary with use. All
battery life numbers provided will be a rough estimate based on usage. Your
exact results will vary somewhat but the same conditions are taken in to
account for each estimate below so expectations should be relative to another
in the list.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not listing these in any particular order. This was intentional to avoid
any personal bias I may have towards the manufacturer or device. While I do
provide my opinion on what the device is best for, I encourage you to read the
summary of each and decide what&amp;rsquo;s best for you.&lt;/p&gt;
&lt;h2 id=&#34;sensecap-t1000-e&#34;&gt;SenseCAP T1000-E&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Best For&lt;/strong&gt; - Every day use in relatively RF dense locations where an antenna would draw unwanted attention or cause logistical issue or protection from the elements is warranted.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html&#34;&gt;SenseCAP Card
Tracker T1000-E&lt;/a&gt; is approximately the size and shape of a typical office badge (about the size of a credit card, but thicker). This radio is nice for people who want an ultra portable and discreet radio that won&amp;rsquo;t draw attention when clipped on to a bag or belt loop. It&amp;rsquo;s IP65 rated meaning it is water resistant. You can safely use this device outdoors without fear of damage from rain or dirt.&lt;/p&gt;
&lt;h3 id=&#34;specifications&#34;&gt;Specifications&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Price: $40 USD&lt;/li&gt;
&lt;li&gt;Radio: Semtech LR1110&lt;/li&gt;
&lt;li&gt;CPU: Nordic nRF52840&lt;/li&gt;
&lt;li&gt;Battery: 700mAh&lt;/li&gt;
&lt;li&gt;Runtime: ~2 days&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;pros&#34;&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Small/Lightweight&lt;/li&gt;
&lt;li&gt;Discreet&lt;/li&gt;
&lt;li&gt;IP65 rated&lt;/li&gt;
&lt;li&gt;Internal GPS receiver&lt;/li&gt;
&lt;li&gt;Good battery life&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cons&#34;&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Internal, fixed Antenna (less range)&lt;/li&gt;
&lt;li&gt;Proprietary Charger&lt;/li&gt;
&lt;li&gt;Not user serviceable&lt;/li&gt;
&lt;li&gt;No built-in display&lt;/li&gt;
&lt;li&gt;No WiFi&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;heltec-t114-v2&#34;&gt;Heltec T114 V2&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Best For&lt;/strong&gt; - Every day use where an external antenna would not be an issue,
protection from the elements can be provided by other means and RF density is
somewhat lower (think suburban environment vs. urban/city)&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://heltec.org/project/mesh-node-t114/&#34;&gt;Heltech T114 V2&lt;/a&gt; is a small,
pocket friendly radio, about the size of a pack of Tic-Tacs or an AirPods case.
This makes it great for sticking in your pocket or in the side pocket of your
backpack. It uses an external SMA connector for the antenna meaning there are
variety of options available to you depending on your specific situation.
Additionally, the T114 comes as a &amp;ldquo;kit&amp;rdquo; of sorts with the board, antenna wire,
battery, GPS module, case and basic antenna. This means the unit is fully user
servicable. This also means, however, that it is not IP rated. While it has
faired relatively well in light rain during my testing, I would not want to
leave it outdoors completely unprotected from the elements for a long time.&lt;/p&gt;
&lt;h3 id=&#34;specifications-1&#34;&gt;Specifications&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Radio: Semtech SX1262&lt;/li&gt;
&lt;li&gt;CPU: Nordic nRF52480&lt;/li&gt;
&lt;li&gt;Battery: Up to ~1000mAh will fit in the official T114 enclosure&lt;/li&gt;
&lt;li&gt;Runtime: ~3 days with a 1000mAh battery&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;pros-1&#34;&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Small size&lt;/li&gt;
&lt;li&gt;User serviceable&lt;/li&gt;
&lt;li&gt;Replaceable battery&lt;/li&gt;
&lt;li&gt;Internal GPS receiver&lt;/li&gt;
&lt;li&gt;Good battery life&lt;/li&gt;
&lt;li&gt;built-in display&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cons-1&#34;&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Somewhat less discreet with it&amp;rsquo;s external antenna&lt;/li&gt;
&lt;li&gt;Not IP rated&lt;/li&gt;
&lt;li&gt;Included antenna is mediocre&lt;/li&gt;
&lt;li&gt;No WiFi&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;muzi-works-r1-neo&#34;&gt;Muzi Works R1 Neo&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Best For&lt;/strong&gt;: The user who needs a premium, bulletproof product and does not
mind paying a bit extra for this.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://muzi.works/products/r1-neo-complete-meshtastic-device&#34;&gt;Muzi Works R1 Neo&lt;/a&gt;
is a newer entrant in the space. The R1 Neo is a new generation of the original
Muzi Works R1.This new model is IP68 rated. It features an enclosure that is a
mixture of machined aluminum and 3D printed PETGCF making it one of the most
durable radios on this list, it is also the only one on this list to be rated
for prolonged submersion in water. In addition, the R1 Neo includes the largest
battery of any radio on this list. All of these features come at a price,
however, as it is the most expensive radio on this list. But, for that you get
a no compromise, portable radio that can handle just about anything you throw
at it.&lt;/p&gt;
&lt;h3 id=&#34;specifications-2&#34;&gt;Specifications&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Radio: Semtech SX1262&lt;/li&gt;
&lt;li&gt;CPU: Nordic nRF52840&lt;/li&gt;
&lt;li&gt;Battery: 1500mAh&lt;/li&gt;
&lt;li&gt;Runtime: ~5 days&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;pros-2&#34;&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;IP68 rated&lt;/li&gt;
&lt;li&gt;High quality external antenna included&lt;/li&gt;
&lt;li&gt;Internal GPS receiver&lt;/li&gt;
&lt;li&gt;Slim/sleek, aluminum and PETGCF case&lt;/li&gt;
&lt;li&gt;Onboard realtime clock&lt;/li&gt;
&lt;li&gt;Builtin notification buzzer&lt;/li&gt;
&lt;li&gt;Hardware GPS disable switch&lt;/li&gt;
&lt;li&gt;Available belt clip holster&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cons-2&#34;&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;High price&lt;/li&gt;
&lt;li&gt;No built-in display&lt;/li&gt;
&lt;li&gt;No WiFi&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;notable-mentions&#34;&gt;Notable Mentions&lt;/h2&gt;
&lt;h3 id=&#34;heltec-lora32-v3v4&#34;&gt;Heltec Lora32 V3/V4&lt;/h3&gt;
&lt;p&gt;The Heltec Lora32 &lt;a href=&#34;https://heltec.org/project/wifi-lora-32-v3/&#34;&gt;V3&lt;/a&gt; and
&lt;a href=&#34;https://heltec.org/project/wifi-lora-32-v4/&#34;&gt;V4&lt;/a&gt; are a great option if you
want a low cost option to get your feet wet in the hobby. However, neither is
included in my list of recommended options above for portable use as they do
not meet one of the key criteria for portable nodes; good battery life. These
boards are based on the ESP32-S3 and consume a &lt;em&gt;substantial&lt;/em&gt; amount of battery
relative to their Nordic nRF52xxx counterparts. In testing, a Lora32 V3 using
a 1000mAh battery will run for &lt;em&gt;optimistically&lt;/em&gt; run for 5hrs. Compare this to
the 3 days of runtime you can get from the same battery on an nRF52xxx based
radio and you can see why I did not include it here. There are two key
advantages to this board, however, over the others. The first is price; the
Lora32 V3 can be found on Amazon for as little as $20 making it one of the
cheapest boards you can find. Additionally, the ESP32-S3, unlike the nRF52xxx
family, supports WiFi. This could be useful if, for example, you wanted to
place a radio somewhere with wifi connectivity to act as an MQTT gateway (a
topic for another day).&lt;/p&gt;
&lt;h2 id=&#34;portable-summary&#34;&gt;Portable Summary&lt;/h2&gt;
&lt;p&gt;In summary, these are only a small handful of devices available on the market
today but I feel that these cover the majority of use &amp;ldquo;portable&amp;rdquo; use cases and
cover a variety of features at different price points.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Multi-instance Meshtasticd Setup</title>
      <link>https://platfrastructure.life/post/meshtasticd-multi/</link>
      <pubDate>Fri, 14 Nov 2025 11:12:53 -0500</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/meshtasticd-multi/</guid>
      <description>&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Running meshtastic on a Raspberry Pi is common place in installations where
you are both stationary and want to run &amp;ldquo;high power&amp;rdquo;. These types of setups
are convenient for a number of reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can power them over PoE&lt;/li&gt;
&lt;li&gt;They typically have higher power output than battery powered devices&lt;/li&gt;
&lt;li&gt;You can attach multiple radios to a single control plane&lt;/li&gt;
&lt;li&gt;You can run &amp;ldquo;virtual&amp;rdquo; radios to provide more than one meshtastic API endpoint
per device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article focuses on the latter two points.&lt;/p&gt;
&lt;h1 id=&#34;why&#34;&gt;Why?&lt;/h1&gt;
&lt;p&gt;When running Meshtastic on a Raspberry Pi, you need to run the &lt;code&gt;meshtasticd&lt;/code&gt;
service. This service is responsible for managing the radios and providing the
API endpoints for your client application. Each instance of &lt;code&gt;meshtasticd&lt;/code&gt; is
only capable of communicating with a single radio and providing a single API
endpoint, which means only one client can connect at a time. But the Pi has
far more horsepower than is necessary to run just a single radio. What if you
want to run multiple radios or you want to have multiple client applications
connecting to a single radio? Enter &lt;code&gt;meshtasticd&lt;/code&gt; multi-instance &amp;ldquo;mode&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;This is less of a &amp;ldquo;mode&amp;rdquo; and more of an architectural concept, but for the
sake of brevity I&amp;rsquo;m going to refer to it as mode throughout this document. The
basic idea is that you will run multiple instances of &lt;code&gt;meshtasticd&lt;/code&gt;
side-by-side on the same Raspberry Pi. Using the recently introduced ability
for network connected meshtastic radios to communicate over UDP multicast, we
can do some really neat things. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Connect two radios to one Pi and use them to &amp;ldquo;bridge&amp;rdquo; two different mesh
presets together.&lt;/li&gt;
&lt;li&gt;Run virtual radios to provide multiple API endpoints for a single radio.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h1&gt;
&lt;h2 id=&#34;hardware&#34;&gt;Hardware&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Raspberry Pi (3/4/5 or Zero/2/2W)&lt;/li&gt;
&lt;li&gt;SD Card (8GB or larger)&lt;/li&gt;
&lt;li&gt;Meshtastic radio modem (&lt;a href=&#34;https://github.com/chrismyers2000/MeshAdv-Pi-Hat&#34;&gt;MeshAdv&lt;/a&gt;, &lt;a href=&#34;https://mtnme.sh/devices/MeshToad/&#34;&gt;MeshToad&lt;/a&gt;, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to the list above, you&amp;rsquo;ll need a way to power the Pi. This can be
done with a dedicated power supply or PoE. Just make sure you are using a
power supply that can provide at least 5V 2A. This is more than the USB port on
your computer can provide so you&amp;rsquo;ll need to use a dedicated power supply or PoE.&lt;/p&gt;
&lt;p&gt;You will also want an antenna for each radio. &lt;strong&gt;Never run your radio without
an antenna. Especially 1w+ radios.&lt;/strong&gt; Doing so can cause irreparable damage to your
radio.&lt;/p&gt;
&lt;h2 id=&#34;software&#34;&gt;Software&lt;/h2&gt;
&lt;p&gt;Since the focus on this article is around running multiple instances of
&lt;code&gt;meshtasticd&lt;/code&gt; I&amp;rsquo;m going to assume you already have a few things in place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A working Linux installation&lt;/li&gt;
&lt;li&gt;A working single-instance &lt;code&gt;meshtasticd&lt;/code&gt; setup w/ at least one radio&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;the-setup&#34;&gt;The Setup&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;meshtasticd&lt;/code&gt; is typically run as a background service. On modern systems,
this is done using systemd. The file that controls the default &lt;code&gt;meshtasticd&lt;/code&gt;
instance is located at &lt;code&gt;/etc/systemd/system/meshtasticd.service&lt;/code&gt;. This file is
responsible for starting the &lt;code&gt;meshtasticd&lt;/code&gt; service and keeping it running. It
is also responsible for restarting the service if it crashes. Unfortunately,
this file (called a unit file) is only designed to run a single instance of
&lt;code&gt;meshtasticd&lt;/code&gt;. In order to run multiple instances, we need to create multiple
unit files. In addition, there are two parameters that must be passed to both
the new &lt;code&gt;meshtasticd&lt;/code&gt; processes in order to allow them to simultaneously
coexist on the same system. The following sections will dissect the standard
unit file and explain how to replicate this file with the necessary changes to
run an additional instance&lt;/p&gt;
&lt;h2 id=&#34;the-unit-file&#34;&gt;The Unit File&lt;/h2&gt;
&lt;p&gt;The default unit file for &lt;code&gt;meshtasticd&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Unit&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Description&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Meshtastic Native Daemon
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;After&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;network-online.target
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;StartLimitInterval&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;StartLimitBurst&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Service&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AmbientCapabilities&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;CAP_NET_BIND_SERVICE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;User&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meshtasticd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Group&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meshtasticd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;simple
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ExecStart&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/usr/bin/meshtasticd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Restart&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;always
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RestartSec&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Install&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WantedBy&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;multi-user.target
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re not going to go in to detail about each of the parts of this file in
this article. If you want to learn more, I recommend &lt;a href=&#34;https://www.man7.org/linux/man-pages/man5/systemd.unit.5.html&#34;&gt;systemd.unit(5)&lt;/a&gt;
for a detailed explanation of the unit file format. We&amp;rsquo;re going to be focusing
on the &lt;code&gt;[Service]&lt;/code&gt; section, specifically the &lt;code&gt;ExecStart&lt;/code&gt; line. This line is
responsible for starting the &lt;code&gt;meshtasticd&lt;/code&gt; process. In order to run multiple
instances, we need to create multiple unit files with slightly different
&lt;code&gt;ExecStart&lt;/code&gt; lines. We need to add the following three parameters to the
&lt;code&gt;ExecStart&lt;/code&gt; line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--port &amp;lt;portnum&amp;gt;&lt;/code&gt; - This parameter specifies the port number that the API
server will listen on. The default port is 8080. You can use any port number
you want as long as it is not already in use.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--config &amp;lt;config_file&amp;gt;&lt;/code&gt; - This parameter specifies the config file to use. We
will create a new config file for each instance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--fsdir &amp;lt;fsdir&amp;gt;&lt;/code&gt; - This parameter specifies the directory to use for storing
the mesh data. We will create a new directory for each instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When we add these parameters, we&amp;rsquo;re going to end up with a unit file that
looks something like this, with &lt;code&gt;&amp;lt;port_number&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;instance_name&amp;gt;&lt;/code&gt;, and
&lt;code&gt;&amp;lt;fsdir&amp;gt;&lt;/code&gt; replaced with the appropriate values:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Unit&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Description&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Meshtastic Native Daemon
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;After&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;network-online.target
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;StartLimitInterval&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;StartLimitBurst&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Service&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AmbientCapabilities&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;CAP_NET_BIND_SERVICE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;User&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meshtasticd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Group&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;meshtasticd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;simple
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ExecStart&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/usr/bin/meshtasticd --port &amp;lt;port_number&amp;gt; --config /etc/meshtasticd/&amp;lt;instance_name&amp;gt;.conf --fsdir /var/lib/&amp;lt;instance_name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Restart&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;always
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RestartSec&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Install&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WantedBy&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;multi-user.target
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;copy-the-file&#34;&gt;Copy the File&lt;/h2&gt;
&lt;p&gt;Copy the original unit file to a new file with a different name. Replace
&lt;code&gt;&amp;lt;instance_name&amp;gt;&lt;/code&gt; with the name you want to give this instance.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo cp /etc/systemd/system/meshtasticd.service /etc/systemd/system/meshtasticd-&amp;lt;instance_name&amp;gt;.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reload the systemd daemon to pick up the new unit file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Edit the new unit file to add the parameters we discussed earlier. Replace
&lt;code&gt;&amp;lt;port_number&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;instance_name&amp;gt;&lt;/code&gt; with the appropriate values.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl edit --full meshtasticd-&amp;lt;instance_name&amp;gt;.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;supporting-files&#34;&gt;Supporting Files&lt;/h2&gt;
&lt;p&gt;In addition to the unit file, we need to create a config file and a directory
for each instance. The config file should be created in
&lt;code&gt;/etc/meshtasticd/&amp;lt;instance_name&amp;gt;.conf&lt;/code&gt;. The directory should be created in
&lt;code&gt;/var/lib/&amp;lt;instance_name&amp;gt;&lt;/code&gt;. You can, more or less, copy the original config file
to the new location and rename it. You should then edit the new config file as
you would like for your second radio.&lt;/p&gt;
&lt;h2 id=&#34;testing&#34;&gt;Testing&lt;/h2&gt;
&lt;p&gt;Once you have created your unit file and config file, you can start the new
instance up with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl start meshtasticd-&amp;lt;instance_name&amp;gt;.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once it is running to your liking, you can enable it to start on boot with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable meshtasticd-&amp;lt;instance_name&amp;gt;.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;That&amp;rsquo;s it! You should now have multiple instances of &lt;code&gt;meshtasticd&lt;/code&gt; running on
your Raspberry Pi. You can connect to each instance using the port number you
specified in the unit file. You can also connect to each instance using the
&lt;code&gt;--port&lt;/code&gt; parameter when running the &lt;code&gt;meshtastic&lt;/code&gt; command line tool. The best
part is, you&amp;rsquo;re not limited to just two instances. The process outlined above
can be repeated as many times as you like.&lt;/p&gt;
&lt;p&gt;Once you have your multi-instance &lt;code&gt;meshtasticd&lt;/code&gt; setup, you may wish to set up
UDP multicast between your radios. There is very little documentation on this
feature at the time of writing but a quick summary of the setup is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure each radio with &lt;code&gt;--set network.enable_protocols 1&lt;/code&gt; using the
meshtastic command line tool.&lt;/li&gt;
&lt;li&gt;Ensure that all radios are on the same network (localhost works)&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>OFAC, GeoIPs and Blocking Unwanted Network Traffic with Python</title>
      <link>https://platfrastructure.life/post/geoiplookup/</link>
      <pubDate>Fri, 20 Dec 2024 10:26:04 -0500</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/geoiplookup/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s the Holiday season and my employer has some pretty strict an clea rules
around changes that can be made during &amp;ldquo;peak seasons&amp;rdquo; which, for our product,
starts after the US Thanksgiving Holiday and continues until after the 1st of
the new year. This has left me with more time than usual to scratch some of the
intellectual itches I have accumulated over the past year. This year I decided
to work on writing some tools to help automate some of the more tedious,
routine tasks that my team is responsible for. This post, however, has nothing
at all to do with that work. But it&amp;rsquo;s important background to know before we
dive in to what this post actually is about.&lt;/p&gt;
&lt;p&gt;My wife and I purchased a new home in March of 2024. One of the key attractions
of the house was that it had a large (~800sq/ft) unfinished and completely open,
daylight basement. As I work from home part time, this was very interesting to
me as a future home office space. Fast forward to July of this year and we
&amp;ldquo;broke ground&amp;rdquo; on finishing the basement and turning the concrete and
cinder block dungeon I had been working in the last few months in to a proper
space in which I could live and work. It&amp;rsquo;s now about 95% complete and it was
time to start re-wiring my home network that included some new (to me)
equipment, a Ubiquiti Security Gateway (USG).&lt;/p&gt;
&lt;h2 id=&#34;i-promise-im-getting-to-the-point&#34;&gt;I promise I&amp;rsquo;m Getting to the Point&lt;/h2&gt;
&lt;p&gt;While setting up the USG I noticed that there was an option to block IP ranges
by country of origin. Being the pragmatic engineer that I am, I understand that
it&amp;rsquo;s simply a reality of The Internet that certain GeoIP ranges produce higher
volumes of unwanted or malicious traffic and that most of these countries are
not countries from which I never expect to see inbound traffic. But I did not
want to blindly block every country&amp;rsquo;s IP range besides the US after all, I do
host a small number of service from my home IP address and I don&amp;rsquo;t know the
exact origin of every user of those services. Enter the Office of Foreign Assets
Control (OFAC) or, more specifically, OFAC&amp;rsquo;s Sanctions List Service (SLS).&lt;/p&gt;
&lt;h2 id=&#34;ofac-and-their-sls&#34;&gt;OFAC and their SLS&lt;/h2&gt;
&lt;p&gt;For those unfamiliar with OFAC or their SLS, here are some brief excerpts from
their website:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Office of Foreign Assets Control administers and enforces economic
sanctions programs primarily against countries and groups of individuals,
such as terrorists and narcotics traffickers. These sanctions can be either
comprehensive or selective, using the blocking of assets and trade restrictions
to accomplish foreign policy and national security goals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Basically, they&amp;rsquo;re the organization, within the United States, which enforces
the sanctions imposed by executive and legislative branches of government. As
part of their efforts in enforcing these sanctions, they provide a service they
call the Sanctions List Service (SLS). Again, quoting from their website:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OFAC&amp;rsquo;s Sanctions List Service (SLS) provides users with easy access to the
most up-to-date Sanctions Lists and Sacntions list data ready for immediate
download&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Effectively, and API for fetching data about current government sanctions.&lt;/p&gt;
&lt;h2 id=&#34;geoips&#34;&gt;GeoIPs&lt;/h2&gt;
&lt;p&gt;The Internet is a big place, on the IPv4 space alone there are 4,294,967,296
unique IP addresses. With the emergence of widespread usage of IPv6, this number
has increased to 340 undecillion (yes, that&amp;rsquo;s apparently an actual number.
That&amp;rsquo;s 340 with 36 zeroes after it or 3.4&lt;em&gt;x10&lt;/em&gt;38 for you nerds out there.).
That&amp;rsquo;s a lot of unique devices and a lot of people you probably don&amp;rsquo;t know.&lt;/p&gt;
&lt;p&gt;The Internet is also a (relatively) organized place. Countries are assigned
blocks of IP addresses by the Internet Assigned Number Authority (IANA). To
save you from having to read it here, if you want to learn more about IANA and
GeoIPs, head over to &lt;a href=&#34;https://geotargetly.com/blog/how-ip-geolocation-works&#34;&gt;Geo
Targetly&lt;/a&gt; to learn more,
it&amp;rsquo;s way more than I want to cover here. For our purposes here, you just need to
know that IANA assigned blocks of IPs to countries and, to a lesser extent,
cities. This information is public and can be found from a number of sources
online. One popular source is &lt;a href=&#34;https://www.maxmind.com/en/home&#34;&gt;MAXMIND&lt;/a&gt;. They
provide a free and paid tier service for downloading up-to-date GeoIP
information. They even have official libraries for many of the major programming
languages.&lt;/p&gt;
&lt;h2 id=&#34;putting-the-pieces-together&#34;&gt;Putting the Pieces Together&lt;/h2&gt;
&lt;p&gt;So, let&amp;rsquo;s see, I wanted to block IPs from countries that have no business on my
network&amp;hellip; but I don&amp;rsquo;t want to blindly block every country, just the ones that
we&amp;rsquo;ll call &amp;ldquo;adversarial&amp;rdquo; with respect to my relative geographical location. We
should be able to do this by sourcing OFAC&amp;rsquo;s most recent sanctions list and
using that to lookup IP addresses in the MaxMind database, right? Sort of&amp;hellip;.&lt;/p&gt;
&lt;p&gt;Now, I may be wrong but I spent quite a while looking for a way to ask the
question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What IP addresses belong to country X?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The issue stems from the format of the free tier MaxMind GeoLite2-Country
database. It is structured with the intention of looking up information by the
IP address&amp;hellip; which is fine, and is definitely in and of itself useful, but it&amp;rsquo;s
not what we want to do here. After much trial and error, it did not seem like it
was going to be possible to efficiently use the GeoIP2 library, with the free
tier database, to ask this question.&lt;/p&gt;
&lt;p&gt;Fortunately, there is a second Python library available called
&lt;a href=&#34;https://pypi.org/project/maxminddb/&#34;&gt;MaxMindDB&lt;/a&gt;. This library is very similar
to the official GeoIP2 library except that they&amp;rsquo;ve implemented an
&lt;code&gt;__iter__()&lt;/code&gt; method to list out the contents of the entire database file. It&amp;rsquo;s
not ideal, but maybe we can work with this.&lt;/p&gt;
&lt;h2 id=&#34;meat-and-potatoes&#34;&gt;Meat and Potatoes&lt;/h2&gt;
&lt;p&gt;Using the above mentioned MaxMindDB library and it&amp;rsquo;s &lt;code&gt;__iter__()&lt;/code&gt; method, we can
construct our own dataset in a format that lets us look up the IP addresses for
countries listed in the OFAC SLS list. It&amp;rsquo;s not quite done yet, but I think it&amp;rsquo;s
&amp;ldquo;done enough&amp;rdquo; to officially announce publicly that I&amp;rsquo;ve written a tool/library
to do just this.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/tedwardd/country-ip-lookup&#34;&gt;Country IP Lookup&lt;/a&gt; project
can consume either the MaxMind Database permalink URL (requires a registered
MaxMind account) or a locally cached copy of one of the MaxMind Country
databases and return the data as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;RU&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;: [&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;a.a.a.a&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;b.b.b.b&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;c.c.c.c&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;US&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;: [&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;x.x.x.x&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;y.y.y.y&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;z.z.z.z&amp;#34;]&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;etc...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this data, you can now take the ISO country codes provided by the OFAC SLS
and return a list of IPs and IP ranges. Additionally, this library can act as a
standalone tool that accepts a country ISO code and returns the IPs and IP
ranges as a newline delimited list, useful for incorporation in shell scripts
and other non-python utilities:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;./lookup.py
  usage: lookup [-h] -c COUNTRY [--config CONFIG_FILE] [-d DB_CACHE]
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I was initially very frustrated when I discovered that there didn&amp;rsquo;t seem to be
an existing way to answer my seemingly simple question. But after tackling this
problem, I am kind of glad that there wasn&amp;rsquo;t. It forced me to think outside the
box and put some of the skills I&amp;rsquo;ve obtained over the last years both as a
security focused systems administrator and now software developer to work. But I
also know that I&amp;rsquo;m a lifelong learner and that I don&amp;rsquo;t know everything there is
to know about Python or the subjects that I touched on in this post. It&amp;rsquo;s
entirely possible that there actually &lt;em&gt;is&lt;/em&gt; a way to produce this same result
with the existing libraries and I just couldn&amp;rsquo;t figure it out. But now I know
that I have a way to do it and it&amp;rsquo;s something I built myself and know how to
use, end-to-end. And I know that if there&amp;rsquo;s ever a time when I want it to work
differently, I&amp;rsquo;m empowered to get my hands dirty again and make it work
differently.&lt;/p&gt;
&lt;p&gt;As for the practicality of this experiment&amp;hellip;. Is this necessary? Probably not.
Do I think my home network is going to be the target of some state sponsored
cyber attack? I certainly hope not. But was it interesting to learn all this?&lt;/p&gt;
&lt;p&gt;You bet.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Portable Dev Environments with Devpods</title>
      <link>https://platfrastructure.life/post/devpods/</link>
      <pubDate>Thu, 20 Jun 2024 14:32:12 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/devpods/</guid>
      <description>&lt;p&gt;If you&amp;rsquo;re not already following &lt;a href=&#34;https://www.youtube.com/@mischavandenburg&#34;&gt;Mischa van den Burg&lt;/a&gt;
on Youtube, go do it now. Mischa has some really good content for developers
and engineers alike. His most recently video (as of the time of this writing) is
on the topic of using &lt;a href=&#34;https://devpods.sh&#34;&gt;devpods.sh&lt;/a&gt; to create a portable
development environment. This got me thinking about how I create my development
environments and if there was a possibility that I could leverage any of the
ideas in my day-to-day work.&lt;/p&gt;
&lt;h1 id=&#34;present-day&#34;&gt;Present Day&lt;/h1&gt;
&lt;p&gt;My current development environment is pretty straight forward in terms of tools.
I use Neovim (Actually I use &lt;a href=&#34;http://www.lazyvim.org/&#34;&gt;LazyVim&lt;/a&gt; but that&amp;rsquo;s a
topic for another day) and that&amp;rsquo;s about it. I have a bunch of customizations and
plugins for it, but at the end of the day, it&amp;rsquo;s just Neovim doing the heavy
lifting.&lt;/p&gt;
&lt;h1 id=&#34;the-motivation&#34;&gt;The Motivation&lt;/h1&gt;
&lt;p&gt;To put it bluntly; shit happens. Since starting with Mailchimp in 2016 I&amp;rsquo;ve had
six laptops. Some of these were replaced after the standard three years but,
in reality most were replaced due to theft, hardware failure or company
acquisition. Each time, it would take me at least a day or more to get back up
and running at 100%. This is wasted time. I need a better way to manage things.&lt;/p&gt;
&lt;h1 id=&#34;early-attempts&#34;&gt;Early attempts&lt;/h1&gt;
&lt;p&gt;Early in my quest to improve this situation I experimented with several options,
some of which I still use to this day. Take, for instance, &lt;a href=&#34;https://github.com/tedwardd/dotfiles&#34;&gt;my dotfiles&lt;/a&gt;. I use
&lt;a href=&#34;https://github.com/anishathalye/dotbot&#34;&gt;dotbot&lt;/a&gt; to manage and bootstrap my
dotfiles in a way that keeps them organized and makes re-installing them a
breeze. But this leaves a few loose ends in the form of installed software
packages. I tried to mitigate this by generating a &lt;code&gt;Brewfile&lt;/code&gt; of all the
software I have installed, but that was never quite comprehensive enough and
seemed to always require a little extra work every time I had to rebuild the
world.&lt;/p&gt;
&lt;p&gt;I eventually stumbled upon &lt;a href=&#34;https://nixos.org/&#34;&gt;Nix&lt;/a&gt;. The idea of having a
standardized way to declare packages and configuration in a single place seemed
like it would be a great idea&amp;hellip; in theory, at least. In practice, I found Nix
to be more than I needed and I was spending more time fighting the configuration
than it was worth. Additionally, using Nix to configure applications, while
genuinely an interesting and good idea in some cases, can be problematic when
you&amp;rsquo;re trying to learn a new program, plugin or configuration. Most online
resources will assume you&amp;rsquo;re configuring the application directly and, while I&amp;rsquo;m
sure someone who is very familiar with Nix configuration would have little
trouble translating, I found it very difficult to learn to configure new
software in Nix as I was usually unfamiliar with both.&lt;/p&gt;
&lt;h1 id=&#34;devcontainers&#34;&gt;Devcontainers&lt;/h1&gt;
&lt;p&gt;Finally, we arrive at the meat of the topic, devcontainers and, more
specifically &lt;a href=&#34;https://devpods.sh&#34;&gt;devpods.sh&lt;/a&gt;. I&amp;rsquo;ve been using containers for a
long time in my development workflow. Usually, it&amp;rsquo;s for building and testing my
code to ensure I&amp;rsquo;m doing so in a sterile environment that I know will work in CI
(since CI does everything in a container too). I&amp;rsquo;ve even gone so far as to try
and execute builds and tests on a remote docker host with varying degrees of
success. But it never occurred to me to move my entire development environment
in to a remote container for the simple reason that it always seemed like it
would be more trouble than it was worth. Now, using devcontainers and devpods, I can easily
define a fully functional environment in just a few steps and while I don&amp;rsquo;t
believe I&amp;rsquo;ve yet fully realized the potential here, I am already definitely
seeing how it could be a powerful option.&lt;/p&gt;
&lt;p&gt;Now, the only thing I need to install on my physical machine is devpods itself,
configure a local or cloud backend provider and clone my code. The rest of it,
my editor, my test suites and my system tools are all ecapsulated inside of the
devcontainer itself and a simple (reusable) setup script used during
initialization of the environment.&lt;/p&gt;
&lt;p&gt;The best part of all of this is that it&amp;rsquo;s platform agnostic. I no longer need to
worry about what OS I&amp;rsquo;m on or if it supports Docker or how much battery life
will suffer from running all these containers. If I&amp;rsquo;m on a low powered device
such as an tablet or Chromebook, I can pull up a terminal Linux terminal,
install the AppImage for devpods, configure Google Cloud, Digital Ocean or even
my home K3S cluster as the backend provider, clone my code and I have the same
exact development environment as I would on my Macbook Pro or desktop computer.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I know I didn&amp;rsquo;t really cover any technical details in this post and that was
actually intentional. Mischa has covered the details of everything I&amp;rsquo;ve talked
about here on his Youtube channel and his
&lt;a href=&#34;https://github.com/mischavandenburg&#34;&gt;Github&lt;/a&gt;. So I highly encourage you to
learn more by going and checking out his content and experimenting with this
yourself.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using direnv to improve your workflow</title>
      <link>https://platfrastructure.life/post/direnv/</link>
      <pubDate>Wed, 01 May 2024 12:52:51 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/direnv/</guid>
      <description>&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;p&gt;For the uninitiated, &lt;a href=&#34;https://direnv.net&#34;&gt;direnv&lt;/a&gt; is a tool that can load and
unload environment variables depending on your current working directory. On
it&amp;rsquo;s surface, this seems like a fairly trivial thing but if you take a closer
look, &lt;code&gt;direnv&lt;/code&gt; packs some really neat features which can really improve your
development workflow.&lt;/p&gt;
&lt;p&gt;Take for example the &amp;ldquo;simple&amp;rdquo; task of including an extra directory in your &lt;code&gt;$PATH&lt;/code&gt;
while working inside your project directory. Without &lt;code&gt;direnv&lt;/code&gt; you&amp;rsquo;re looking at
something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cd project_dir/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ export OLD_PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$PATH
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ export PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;./bin:$PATH
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; some stuff&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cd ..
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ export PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$OLD_PATH
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;envrc&#34;&gt;Envrc&lt;/h2&gt;
&lt;p&gt;Now, we could certainly write two scripts to do this for us and run them
whenever we work in our project environment but we still have to remember to
run them when we enter and exit a project environment. What if we had something
to handle that for us; enter &lt;code&gt;.envrc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead of having a set of scripts to set up and tear down your environment
manually, let&amp;rsquo;s put the necessary bits in to a file named &lt;code&gt;.envrc&lt;/code&gt; within the
root of our project directory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;./bin:$PATH
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now tell direnv it&amp;rsquo;s ok to load the file with &lt;code&gt;direnv allow&lt;/code&gt; and that&amp;rsquo;s it!
Whenever you enter this directory in the future, direnv will change the value of
&lt;code&gt;$PATH&lt;/code&gt; for you. But wait, IT GETS BETTER!&lt;/p&gt;
&lt;p&gt;The above example works but direnv has some extra tricks up it&amp;rsquo;s sleeve to make
this process even easier and less error prone.&lt;/p&gt;
&lt;h2 id=&#34;direnv-stdlib&#34;&gt;DIRENV-STDLIB&lt;/h2&gt;
&lt;p&gt;Direnv provides a &lt;a href=&#34;https://direnv.net/man/direnv-stdlib.1.html&#34;&gt;stdlib&lt;/a&gt; of
common functions that make working with direnv easier and less prone to common
mistakes. Using the stdlib, let&amp;rsquo;s improve our example above. Replace the
contents of the &lt;code&gt;.envrc&lt;/code&gt; with the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PATH_add bin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will need to re-allow direnv with &lt;code&gt;direnv allow&lt;/code&gt; after any modifications to
a &lt;code&gt;.envrc&lt;/code&gt; file. Once you&amp;rsquo;ve done this, you can leave and re-enter the directory
and your path will be updated to include &lt;code&gt;/my/project/bin&lt;/code&gt;. Using &lt;code&gt;PATH_add&lt;/code&gt; has
several advantages over directly setting your &lt;code&gt;$PATH&lt;/code&gt;. Primary among them being
that it avoids common mistakes such as &lt;code&gt;export PATH=bin&lt;/code&gt; which would remove all
other values of &lt;code&gt;$PATH&lt;/code&gt; (effectively breaking your environment).&lt;/p&gt;
&lt;h1 id=&#34;advanced-trickery&#34;&gt;Advanced Trickery&lt;/h1&gt;
&lt;p&gt;We have seen how direnv can modify environment variables and we&amp;rsquo;ve learned that
direnv stdlib has some builtin functionality to make our lives either. Now,
let&amp;rsquo;s look at some of the more interesting aspects of direnv and how they can be
used to improve your development environment.&lt;/p&gt;
&lt;h2 id=&#34;nested-environments&#34;&gt;Nested Environments&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say we have a project with some subdirectories, maybe it looks something
like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── bin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── file2.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── bin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ├── file.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        └── file2.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want to add &lt;code&gt;project/bin&lt;/code&gt; to our path but if we go in to &lt;code&gt;project/foo/&lt;/code&gt; we
want to also add &lt;code&gt;project/foo/bin&lt;/code&gt; as well. If we create a &lt;code&gt;.envrc&lt;/code&gt; inside of
&lt;code&gt;project/&lt;/code&gt; and inside of &lt;code&gt;project/foo/&lt;/code&gt; each containing &lt;code&gt;PATH_add bin&lt;/code&gt; we are
going to have a problem. In this scenario, direnv will load &lt;code&gt;project/foo/.envrc&lt;/code&gt;
when we navigate to &lt;code&gt;project/foo/&lt;/code&gt; but in doing so, it is going to unload the rc
file from &lt;code&gt;project/&lt;/code&gt;. If you guessed the stdlib has a solution for this, you&amp;rsquo;re
absolutely right; it has two options, in fact: &lt;code&gt;source_up .envrc&lt;/code&gt; and
&lt;code&gt;source_up_if_exists .envrc&lt;/code&gt;. Including one of these options in the rc file for
&lt;code&gt;project/foo/&lt;/code&gt; will enable us to include both files. The following is an example
of the &lt;code&gt;project/foo/.envrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;source_up .envrc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PATH_add bin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s important to remember that these rc files are read from top to bottom in
this case. If you had &lt;code&gt;project/bin/my_app&lt;/code&gt; and &lt;code&gt;project/foo/bin/my_app&lt;/code&gt; and
wanted the most specific to be used when running &lt;code&gt;my_app&lt;/code&gt;, you need to ensure
you include &lt;code&gt;PATH_add bin&lt;/code&gt; &lt;em&gt;after&lt;/em&gt; sourcing the previous directories rc since
doing so is equivalent to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PATH_add ../bin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PATH_add bin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and &lt;code&gt;PATH_add&lt;/code&gt; prepends the specified directory to the front of $PATH.&lt;/p&gt;
&lt;h2 id=&#34;custom-functions&#34;&gt;Custom functions&lt;/h2&gt;
&lt;p&gt;In addition to the stdlib, direnv provides a way to create custom functions
inside of &lt;code&gt;~/.config/direnv/direnvrc&lt;/code&gt;. Inside this file you can define standard
bash functions that you want to have available to any &lt;code&gt;.envrc&lt;/code&gt; file, allowing
you to extend the functionality of direnv. Let&amp;rsquo;s now take look at one such use
case for custom functionality within direnv.&lt;/p&gt;
&lt;h3 id=&#34;per-directory-command-aliases-sort-of&#34;&gt;Per Directory Command Aliases (sort of)&lt;/h3&gt;
&lt;p&gt;Unfortunately, as of &lt;a href=&#34;https://github.com/direnv/direnv/issues/73&#34;&gt;today&lt;/a&gt;, there
is no native method for loading additional shell aliases inside of a &lt;code&gt;.envrc&lt;/code&gt;
file. However, using custom functions we can kind of fake it. While this does
not, strictly speaking, create shell aliases, it does provide a method for
creating alias-like commands inside of your &lt;code&gt;.envrc&lt;/code&gt; file. The one downside to
this approach is that it will create and leave files behind within your project
directory. These are easy enough to ignore with a &lt;code&gt;.gitignore&lt;/code&gt; file and I&amp;rsquo;ve
seen some examples of ZSH scripts which can handle the load/unload events
outside of direnv but I will not be looking at those today. If you want to read
more, check out the comments on &lt;a href=&#34;https://github.com/direnv/direnv/issues/129&#34;&gt;this
PR&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The meat of this is in the custom function. You&amp;rsquo;ll place the following function
inside of your &lt;code&gt;~/.config/direnv/direnvrc&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export_alias&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  local name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  shift
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  local alias_dir&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$PWD/.direnv/aliases
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  local target&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$alias_dir&lt;span style=&#34;color:#e6db74&#34;&gt;/&lt;/span&gt;$name&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  local oldpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PATH&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mkdir -p &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$alias_dir&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ! &lt;span style=&#34;color:#f92672&#34;&gt;[[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&lt;/span&gt;$PATH&lt;span style=&#34;color:#e6db74&#34;&gt;:&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; *&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;:&lt;/span&gt;$alias_dir&lt;span style=&#34;color:#e6db74&#34;&gt;:&amp;#34;&lt;/span&gt;* &lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PATH_add &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$alias_dir&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#!/usr/bin/env bash&amp;#34;&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$target&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PATH=\&amp;#34;&lt;/span&gt;$oldpath&lt;span style=&#34;color:#e6db74&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$target&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$@&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$target&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  chmod +x &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$target&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Using Terraform with libvirt in 2022</title>
      <link>https://platfrastructure.life/post/lessons_with_terraform_libvirt/</link>
      <pubDate>Sat, 24 Dec 2022 13:07:00 +0000</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/lessons_with_terraform_libvirt/</guid>
      <description>&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Following on with the theme of &amp;ldquo;looking for things to use my new server for&amp;rdquo; I
decided to look and see what other options were out there for managing and
provisioning VMs. While Vagrant and Ansible together are fantastic at this,
Vagrant really isn&amp;rsquo;t a good option for &amp;ldquo;production&amp;rdquo; VMs (production in quotes
here because&amp;hellip; it&amp;rsquo;s my lab server and production is quite relative in that
context). I use a lot of Terraform in my day job so I thought I&amp;rsquo;d look to see
if there was a libvirt or kvm provider for it, turns out there is but it&amp;rsquo;s a
little&amp;hellip; well, we&amp;rsquo;ll call it undermaintained as it&amp;rsquo;s not quite dead but the
development of it seems to have slowed considerably in the last year. You can
find the provider
&lt;a href=&#34;https://github.com/dmacvicar/terraform-provider-libvirt&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, credit where credit is due. This provider works very well, it&amp;rsquo;s just that
it appears to be a side project for a single developer and we all know we have
infinite time for these kinds of things so it&amp;rsquo;s probably just something that&amp;rsquo;s
fallen on the back burner for one reason or another. That said, with a little
tinkering and outside the box thinking, we can still use this provider today,
here&amp;rsquo;s how.&lt;/p&gt;
&lt;h1 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h1&gt;
&lt;p&gt;You&amp;rsquo;ll need a couple of things to get started:&lt;/p&gt;
&lt;h2 id=&#34;terraform&#34;&gt;Terraform&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve come to like &lt;a href=&#34;https://github.com/tfutils/tfenv&#34;&gt;tfenv&lt;/a&gt; for installing and
managing Terraform executables on my system. I recommend installing it and then
using it to install the latest Terraform version. Installation steps are in the
&lt;code&gt;README.md&lt;/code&gt; found at the link above but here&amp;rsquo;s a summary for the lazy folks who
are also on MacOS.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ brew install tfenv
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I forget whether or not this adds it to your &lt;code&gt;~/.bash_profile&lt;/code&gt; automatically or not so
read the message brew gives you when the install is finished. You&amp;rsquo;ll need to add
or confirm that the following line was added to it.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ grep tfenv ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If not, add it&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ test -d $HOME/.tfenv || mkdir $HOME/.tfenv
$ echo &amp;#39;export PATH=&amp;#34;$HOME/.tfenv/bin:$PATH&amp;#34;&amp;#39; &amp;gt;&amp;gt; ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now install the latest Terraform version and activate it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ tfenv install
$ tfenv use 1.3.6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you&amp;rsquo;re following this tutorial in the future, you may have to run &lt;code&gt;tfenv list&lt;/code&gt;
to see what &amp;ldquo;latest&amp;rdquo; version was that was installed and substitute that version
for the one shown above.&lt;/p&gt;
&lt;h2 id=&#34;terraform-provider-libvirt&#34;&gt;Terraform-provider-libvirt&lt;/h2&gt;
&lt;p&gt;The libvirt provider is configured and used just like any other Terraform
provider. Here is an example directly from the project README:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;terraform {
  required_providers {
    libvirt = {
      source = &amp;#34;dmacvicar/libvirt&amp;#34;
    }
  }
}

provider &amp;#34;libvirt&amp;#34; {
  # Configuration options
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You&amp;rsquo;ll notice, however, that if you go through the examples in the repo that
they&amp;rsquo;re organized by Terraform version and that the newest version they have
listed is 0.13&amp;hellip; well, we&amp;rsquo;ve moved a bit beyond that so you&amp;rsquo;re going to find
that some of the examples don&amp;rsquo;t work perfectly, thankfully, yours truly has
spent some time with this and figure out at least one issue and how to work
around it:&lt;/p&gt;
&lt;p&gt;Inside of the 0.13 Ubuntu example there are two template files used, one for
the network config and one for the cloud-init config. The example uses the, now
deprecated, &lt;code&gt;template_file&lt;/code&gt; data type. We can safely remove these data objects
and replace the references to them in the later &lt;code&gt;libvirt_cloudinit_disk&lt;/code&gt;
resource with the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;resource &amp;#34;libvirt_cloudinit_disk&amp;#34; &amp;#34;commoninit&amp;#34; {
  name           = &amp;#34;commoninit.iso&amp;#34;
  user_data      = templatefile(&amp;#34;${path.module}/cloud_init.cfg&amp;#34;, {})
  network_config = templatefile(&amp;#34;${path.module}/network_config.cfg&amp;#34;, {})
  pool           = libvirt_pool.ubuntu.name
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should now allow the Ubuntu 0.13 example to work with &amp;gt;= 1.0.&lt;/p&gt;
&lt;h2 id=&#34;libvirt-permissions-problems&#34;&gt;Libvirt permissions problems&lt;/h2&gt;
&lt;p&gt;The last issue I encountered with the Ubuntu example was:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;libvirt_pool.ubuntu: Refreshing state... [id=1422d683-633e-4c85-abfc-c182bbd46b43]
libvirt_cloudinit_disk.commoninit: Refreshing state... [id=/tmp/terraform-provider-libvirt-pool-ubuntu/commoninit.iso;12cd7610-9528-439c-8884-c702aa29984a]
libvirt_volume.ubuntu-qcow2: Refreshing state... [id=/tmp/terraform-provider-libvirt-pool-ubuntu/ubuntu-qcow2]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;when attempting to apply. Everything appeared to get created properly on the
target server but the domain wouldn&amp;rsquo;t start up. Well, it turns out this is not
a libvirt provider issue but, instead, an issue with the qemu configuration
shipped with Ubuntu 22.04 by default. I&amp;rsquo;m not 100% sure if this is because I
don&amp;rsquo;t have selinux or apparmor installed on the server and the qemu package
for Ubuntu expects that it&amp;rsquo;s enabled or if this is legitimately a bug in the
pre-packaged qemu installation for Ubuntu, but either way, it&amp;rsquo;s an easy fix.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t have apparmor or selinux installed on your system, simple edit
&lt;code&gt;/etc/libvirt/qemu.conf&lt;/code&gt; and search for the &lt;code&gt;security_driver&lt;/code&gt; directive. If you
have selinux installed, set it to &amp;ldquo;selinux&amp;rdquo;. If you have apparmor installed,
set it to &amp;ldquo;apparmor&amp;rdquo; and if you have neither installed, set it to &amp;ldquo;none&amp;rdquo;. Once
you&amp;rsquo;ve set that parameter, save and close the file and restart the libvirtd
service:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ sudo systemctl restart libvirtd.service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once the service is restarted, issuing &lt;code&gt;virsh start &amp;lt;domain&amp;gt;&lt;/code&gt; should along with
subsequent &lt;code&gt;terraform apply&lt;/code&gt; runs made to create additional new domains.&lt;/p&gt;
&lt;h1 id=&#34;additional-notes-and-conclusion&#34;&gt;Additional Notes and Conclusion&lt;/h1&gt;
&lt;p&gt;One additional thing to make note of, and I&amp;rsquo;m not exactly sure yet if this is a shortcoming of the provider or if I just need to look around for something I haven&amp;rsquo;t yet found in the docs, is that &lt;code&gt;terraform destroy&lt;/code&gt; did not completely clean up the created domain. I found that after &lt;code&gt;terraform apply&lt;/code&gt; ran to create a domain, subsequent destroy and apply commands would fail with the system complaining that the target domain already existed. I&amp;rsquo;ve yet to find a way to have Terraform fully clean this up and have had to either use Ansible or manually ssh to the KVM host and undefine the domain.&lt;/p&gt;
&lt;p&gt;And in conclusion, I think the libvirt provider for Terraform needs some work. I am certainly glad it exists but with how little attention the project seems to be receiving, currently, and how many issues I ran in to it while trying to configure a simple example domain, I am not sure I&amp;rsquo;d want to use it for production. The right answer here is probably to use Packer to build a static VM image, Ansible to build and deploy the domain and image and Cloud-init to do any first-run steps that can&amp;rsquo;t be built in to the vm image during build with packer.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to use Vagrant on an M1 Macbook with a remote kvm server with Vagrant-libvirt</title>
      <link>https://platfrastructure.life/post/fixing_vagrant-libvirt-m1/</link>
      <pubDate>Fri, 23 Dec 2022 22:45:55 +0000</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/fixing_vagrant-libvirt-m1/</guid>
      <description>&lt;h1 id=&#34;house-keeping&#34;&gt;House Keeping&lt;/h1&gt;
&lt;p&gt;First, some housekeeping. I have not been posting very actively. This is not an
apology with a promise to do better. This is an apology with an adjustment of
expectations. My life tends to sporadically get very busy for weeks or months at
a time with a few days or weeks in between of nothing. This makes it difficult
to find time to write on any regular basis. With that said, I want to let you,
dear reader, know now that my blog is not updated regularly or, sometimes, even
very often. Just because there is a stream of posts that come out back-to-back
over the course of a month does not mean that pace of content will continue. If
months, or heck, even a year goes by without anything new here, I&amp;rsquo;m probably
still alive. The best thing to do is to add me to your RSS reader (yes, I said
it, use an RSS reader, it&amp;rsquo;ll change your life) and get notified when I post
instead of having to check back here all the time in case I put something new up.&lt;/p&gt;
&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I ran across a terribly confounding issue today with the vagrant-libvirt
provider while looking for a solution to the lack of virtualization software on
my M1 Macbook. To summarize, Homebrew on an M1 Macbook seems to be a little bit
split brained when it comes to the packaged architectures it provides, despite
having Homebrew installed in the, native, ARM configuration. To my surprise,
this issue also seems to be little documented online at this time. This post is
my attempt to contribute to what&amp;rsquo;s out there on the subject. Hopefully, it saves
the next poor soul dealing with this a great deal of time. As always, if you are
that poor soul, please reach out to me. I love hearing from my readers!&lt;/p&gt;
&lt;h1 id=&#34;the-problem&#34;&gt;The Problem&lt;/h1&gt;
&lt;p&gt;Apple has done a wonderful thing, they have brought ARM adoption to the
mainstream and this has brought a flurry of adoption in the software ecosystem
around this (superior) architecture. For 99.9% of workloads, an Macbook Pro
with either an M1, or now the M2, processor is a wonderful bit of kit. It&amp;rsquo;s
fast, it provides fantastic battery life and it runs much cooler which means
less fan noise for cooling! Unfortunately, a processor architecture shift like
this being (thankfully) such a rare occurrence in the modern computing world
means that change is slow. Apple did a fantastic job with Rosetta 2 and for
most things I hardly noticed anything had changed&amp;hellip; mostly&lt;/p&gt;
&lt;p&gt;The problems only arise when you&amp;rsquo;re trying to do relatively low level work,
such as virtualization. Xhyve is, currently, the only truly viable virtualization
platform for Apple silicon at the time of this writing. Both Virtualbox and
VMWare Fusion have little or no support for it yet, and understandably so&amp;hellip; but
progress is slow. This means that developers have only a few options for creating
development environments. The most common replacement for Vagrant for local
development and testing environments has been docker. And this works very well
for some or many cases but what if we need to test something that can&amp;rsquo;t run in
a container or we need to test something that isn&amp;rsquo;t containerized in production,
is testing in a container really a simulation of production in this case?&lt;/p&gt;
&lt;p&gt;My opinion is that the answer to these questions is no. Sometimes, you still
need a VM to properly test software or a configuration change before pushing to
prod. So, what do you do? My solution was actually, on the surface, quite simple.
Use the vagrant-libvirt provider to connect to my KVM server and run my tests
there. Sure, it&amp;rsquo;s not really &amp;ldquo;local&amp;rdquo; dev in that case, but the latency is
generally low enough and, honestly, there&amp;rsquo;s more horsepower on that server, that
it&amp;rsquo;s probably going to be a win-win. Here, however, is where the problems start.&lt;/p&gt;
&lt;p&gt;The general idea was to install Vagrant and libvirt from Homebrew and then use
the vagrant command-line utility to download the vagrant-libvirt provider.
Unfortunately, if you have Homebrew setup to download native ARM builds, you will
download the ARM build of libvirt but you still get the amd64 build of Vagrant.&lt;/p&gt;
&lt;p&gt;You are probably thinking, as I was, that this wouldn&amp;rsquo;t be an issue. Well, no&amp;hellip;&lt;/p&gt;
&lt;p&gt;The problem is that you have an AMD64 Vagrant trying to use ARM64 libraries&amp;hellip;
that ain&amp;rsquo;t gonna work.&lt;/p&gt;
&lt;h1 id=&#34;the-solution&#34;&gt;The Solution&lt;/h1&gt;
&lt;p&gt;If you have read this far, I applaud you. I ramble a lot and, before I ramble more
here&amp;rsquo;s the TL;DR&lt;/p&gt;
&lt;h2 id=&#34;solution-tldr&#34;&gt;Solution TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Uninstall Vagrant from homebrew&lt;/li&gt;
&lt;li&gt;Download Vagrant from github and bundle install it&lt;/li&gt;
&lt;li&gt;???&lt;/li&gt;
&lt;li&gt;Profit&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;solution-in-detail&#34;&gt;Solution in Detail&lt;/h2&gt;
&lt;p&gt;Since there is no native ARM binary of Vagrant (how&amp;rsquo;s about we get on that,
there, Hashicorp, it&amp;rsquo;s only been&amp;hellip; 3 years?) you are going to have to build it
from source and when I say from source, I mean with Ruby (audibly gags).&lt;/p&gt;
&lt;h3 id=&#34;step-1---dependencies&#34;&gt;Step 1 - Dependencies&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;re going to need a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ruby &amp;gt;= 2.7 (I use rbenv. YMMV)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;step-2---buildinstall-vagrant-from-source&#34;&gt;Step 2 - Build/Install Vagrant from Source&lt;/h3&gt;
&lt;p&gt;Once you&amp;rsquo;ve got all of the dependencies installed and set up (no, I&amp;rsquo;m not going
to show you how. This post is getting long enough), navigate in to the
directory in to which you cloned the vagrant repo and run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ bundle install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If all goes well, then you got everything you needed to install vagrant and
should now see a bin directory inside the repo. But wait, there&amp;rsquo;s more. We now
need to create the binstubs (I&amp;rsquo;m sorry&amp;hellip; what&amp;hellip; Ruby, install didn&amp;rsquo;t just
&amp;ldquo;work&amp;rdquo;?)&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$  bundle --binstubs exec
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will produce an exec directory and &lt;code&gt;exec/vagrant&lt;/code&gt;. This is your new,
bespoke, Vagrant binary. For simplicity sake, I symlinked it to my &lt;code&gt;$HOME/bin&lt;/code&gt;
directory. Anywhere in your $PATH is fine, though&amp;hellip; or just hate yourself and
execute it from the exec directory manually, whatever goats your float.&lt;/p&gt;
&lt;h3 id=&#34;step-3---install-the-vagrant-libvirt-provider&#34;&gt;Step 3 - Install the vagrant-libvirt Provider&lt;/h3&gt;
&lt;p&gt;If everything went smoothly up to here, the following command should &amp;ldquo;Just Work&amp;rdquo;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ vagrant plugin install vagrant-libvirt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There was an additional, longer, command that I saw recommended in a few places
which passed some custom environment variables in to the process. I&amp;rsquo;ll share an
example of that below in case you need it:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CONFIGURE_ARGS=&amp;#34;with-libvirt-lib=$(brew --prefix libvirt)/lib with-libvirt-include=$(brew --prefix libvirt)/include&amp;#34; vagrant plugin install vagrant-libvirt
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;writing-your-vagrant-file&#34;&gt;Writing your vagrant file&lt;/h1&gt;
&lt;p&gt;The last part is easy compared to what we went through above. Setting up the
Vagrantfile. Here&amp;rsquo;s an example of what I use for an Ubuntu 22.04 VM:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;VAGRANTFILE_API_VERSION = &amp;#34;2&amp;#34;

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  config.vm.box = &amp;#34;generic/ubuntu2204&amp;#34;

  config.vm.provider :libvirt do |libvirt|
    libvirt.host = &amp;#34;my_hostname&amp;#34;
    libvirt.username = &amp;#34;my_user&amp;#34;
    libvirt.driver = &amp;#34;kvm&amp;#34;
    libvirt.connect_via_ssh = true
    libvirt.id_ssh_key_file = &amp;#34;/Users/my_user/.ssh/id_ed25519&amp;#34;
  end

end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, running &lt;code&gt;vagrant up&lt;/code&gt; should download the box file locally, copy it to the
KVM server and spin up your VM on the remote host. EZ-PZ.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;OMG WTF WHY IS EVERYTHING LIKE THIS. CAN&amp;rsquo;T WE JUST HAVE SOFTWARE THAT WORKS!!!11&lt;/p&gt;
&lt;p&gt;But, all kidding aside, this was annoying. I wish we could just have nice things
but sometimes we have to make them ourselves.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Final Day in SF: Post Next 2019</title>
      <link>https://platfrastructure.life/post/finaldaysf-2019/</link>
      <pubDate>Sat, 13 Apr 2019 17:57:17 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/finaldaysf-2019/</guid>
      <description>&lt;p&gt;This post has nothing to do with the conference or technology. It will also be
brief. I wanted to give a huge shout out to the San Francisco skating community.
I was a stranger in their community and they welcomed me with open arms. Having
spent a week in San Francisco I came to feel like most of the people there were
cold. Rarely did I experience a warm smile on the street, a friendly face. It
was very refreshing to find that not everyone in the city is like this. Maybe
it&amp;rsquo;s something about people drawn to inline skating, I&amp;rsquo;m not sure, but I will be
forever thankful of the San Francisco skate community for the warm smiles, the
hugs and the laughter we shared on my final day in the city.&lt;/p&gt;
&lt;p&gt;Friday was a work day for me. Nothing interesting to report there. The fun
started around 6PM local time. Strapped on my skates and headed out to explore.
I spent about an hour skating around, finding alleyways and hidden parts of the
city not normally found unless on foot. The city has a lot of interesting spots
to explore on skates. I highly encourage anyone visiting to try it sometime.
Just put your skates on and let the whims take you where they will. I&amp;rsquo;m sure you
won&amp;rsquo;t be disappointed.&lt;/p&gt;
&lt;p&gt;After about an hour of exploration I headed over to the Ferry Building; the
meeting spot for the Friday night skate. The skate wasn&amp;rsquo;t expected to start for
a while so there was only one other skater there. We chatted and instantly
became friends. He showed me a great food spot not far from the meetup spot
where I was able to grab a quick bite before the skate started.&lt;/p&gt;
&lt;p&gt;People started to filter not long after we got back from the food run. We danced
in the square for an hour waiting for the rest of the gang to show up. Once the
time arrived we set out for what was sure to be a fun journey across the city.
Taking about three hours, we covered 12 miles of San Francisco. We hit several
scenic sites, some great photo spots. Got to bomb a few hills and storm a few
tunnels.&lt;/p&gt;
&lt;p&gt;After the main event concluded, about half of the group stuck around for a game
of roller hockey in the square. This was some of the most fun I&amp;rsquo;ve had in a long
time. We played for about an hour, concluding around 1AM. After the conclusion
of the hockey game, I spent another hour exploring the city some more. Let me
tell you, at 1AM, San Francisco, specifically the financial district, is a
skaters paradise. Some really great spots to explore and almost no cars or
pedestrians to worry about.&lt;/p&gt;
&lt;p&gt;If you are ever in San Francisco on a Friday night, I highly encourage you to
check out the night skate. The group is very welcoming and friendly. The route
is about as easy as any route in a city of hills can be and the after skate
activities are second to none.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Google Next 2019: Day 4</title>
      <link>https://platfrastructure.life/post/next2019-04/</link>
      <pubDate>Fri, 12 Apr 2019 01:22:54 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/next2019-04/</guid>
      <description>&lt;p&gt;Day four, the final day. Well, the final day of the actual conference, anyway.
The day started like the others. Wake up, go for a run, grab some coffee on the
walk to the conference. Probably the best part of the day, to be honest. Not
that today was particularly bad or anything but it&amp;rsquo;s hard to beat coffee and
stroll through downtown SF.&lt;/p&gt;
&lt;p&gt;The first talk on the schedule today was a security track about identity
management in GCP. Unfortunately, due to timezone struggles, I slept in too late
and didn&amp;rsquo;t make it. I&amp;rsquo;m sure the talk was great but I have no ability to report
anything about it.&lt;/p&gt;
&lt;p&gt;I had two other talks scheduled for the day. One of them was at 11:40PM, &lt;em&gt;GCP:
Move Fast and Don&amp;rsquo;t Break Things&lt;/em&gt;. The second was 1:15PM titled &lt;em&gt;Repeatable GCP
Environments at Scale With Cloud Build Infra-As-Code Pipelines&lt;/em&gt;. The astute
reader at this point is probably wondering when I got lunch in between this&amp;hellip;
well, I had to sacrifice one of them. I went to the talk on repeatable builds.&lt;/p&gt;
&lt;p&gt;The repeatable builds talk was actually really good. They talked about using
Terraform, as well as Deployment Manager, to define your infrastructure in code
and use cloud builds with forseti policy enforcement to do some sanity checking
and build tests before deploying to production. Overall, I came away with a few
nice tidbits of information that will hopefully help improve our overall GCP
experience.&lt;/p&gt;
&lt;p&gt;After the talks were over the team went out for drinks with some Googlers. Then
a few of us went over to Berkeley, CA for dinner.&lt;/p&gt;
&lt;p&gt;I have one full day left in SF. I have to work from the office for the day but I
hope to have enough time to go see a few things before I leave.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Google Next 2019: Day 3</title>
      <link>https://platfrastructure.life/post/next2019-03/</link>
      <pubDate>Wed, 10 Apr 2019 20:25:16 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/next2019-03/</guid>
      <description>&lt;p&gt;Day three was jam packed with some really interesting talks. It started off with
a talk titled &lt;em&gt;Develop Faster on Kubernetes with Google Container Tools and
Cloud Build&lt;/em&gt;. After lunch, I attended a few of the smaller sessions in more
informal fireside chat spaces. These later talks mainly focused on building
devops teams and utilizing good devops practices within an organization.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;theme of the day&lt;/em&gt; for me, it seemed, was how to utilize Google tools and
infrastructure as code to streamline infrastructure automation. I&amp;rsquo;m sure this
was somewhat intentional at a subconscious level as that&amp;rsquo;s been my primary area
of focus at work recently. To this end, &lt;em&gt;Develop Faster on Kubernetes with Google Container
Tools and Cloud Build&lt;/em&gt; was particularly interesting. They discussed using
&lt;a href=&#34;https://github.com/GoogleContainerTools/skaffold&#34;&gt;Skaffold&lt;/a&gt; in conjunction with
the Skaffold integration in VSCode and Cloud Build to allow developers to
quickly iterate on a project without the need to constantly run &lt;code&gt;kubectl&lt;/code&gt; and
&lt;code&gt;docker&lt;/code&gt; commands to rebuild their changes (Now I just need to find something
like this for &lt;code&gt;vim&lt;/code&gt; ;) ).&lt;/p&gt;
&lt;p&gt;The fireside chats later in the day covered a couple of different topics. The
first one was mainly about how to build a devops team and form a culture that
promotes collaboration across teams. The next session covered &lt;em&gt;Service Levels
and Error Budgets&lt;/em&gt;. I found this talk particularly interesting as it&amp;rsquo;s something
we&amp;rsquo;ve been working on at Mailchimp for a while now; finding a balance between
pushing new production features while also maintaining a very high level of
service availability for our global customer base. One point that I found most
interesting was that &lt;a href=&#34;https://cloud.google.com/stackdriver/&#34;&gt;Stackdriver&lt;/a&gt;
provides convenient tools for calculating your error budgets based on collected
metrics. I could definitely see this being useful as we continue to improve our
processes around this.&lt;/p&gt;
&lt;p&gt;Finally, the day wrapped up with another small talk called &lt;em&gt;Batteries Included
Devops&lt;/em&gt;. This talk felt like a higher level continuation of the previous talk,
focusing heavily on how Google Cloud provides tools out of the box for making
the right devops practices convenient or automatic.&lt;/p&gt;
&lt;p&gt;After the talks completed I headed out for a nice Burrito with several other
Mailchimp engineers over in the Mission District. It was chilly out but nice to
hang out in the park with my coworkers and talk about stuff that wasn&amp;rsquo;t work
related; a rare treat.&lt;/p&gt;
&lt;p&gt;I look forward to tomorrow. Another jam packed day full of learning and fun.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Google Next 2019: Day 2</title>
      <link>https://platfrastructure.life/post/next2019-02/</link>
      <pubDate>Wed, 10 Apr 2019 12:16:56 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/next2019-02/</guid>
      <description>&lt;p&gt;Day two started in the best way possible, with a sunrise skate around the bay.
I met up with two of my friends in the area and spent about an hour skating
around the west bay. Starting at the Ferry Building and just rolling around,
enjoying the weather. I can&amp;rsquo;t think of any better way to start the day. But it
couldn&amp;rsquo;t last forever, I am here for Google Next after all.&lt;/p&gt;
&lt;p&gt;The day started with the first keynote of the conference. I missed a good bit
of it because I was skating (oops!) but the last twenty minutes of it were
filled with buzzwords and the usual enterprise marketing speak, so I assume I
didn&amp;rsquo;t miss much.&lt;/p&gt;
&lt;p&gt;After the keynote ended I wandered around the vendor hall for a little while.
Bumped in to a few friends that were running the booths for their companies and
had a good opportunity to catch up with them. I also took a moment to enter in
to a few raffles; you never know, you might get some free stuff, right!? After
wandering around the vendor hall for a little while, I went looking for lunch.
The lunches at Google Next are generally box lunches; sandwiches, salads, etc.
This year was no different; I had a chicken sandwich.&lt;/p&gt;
&lt;p&gt;After lunch I went to the first actual talk session. This was on CI/CD in
Kubernetes. I was left very disappointed. I was hoping this talk would be a bit
more technical than it was. The speaker ended up mentioning a lot of tools but
the part I was hoping to learn, how to link them together in ways that solve
complex issues around CI/CD pipelines, was really not covered in any great
depth.&lt;/p&gt;
&lt;p&gt;The last talk I attended for the day was much better than the last talk. A
discussion on access controls in GCP. I was very pleased with the level of
technical detail that the speaker included. It got off to a slow start but I
appreciated that the speaker was taking the time to cover the basic concepts
for some of the less experienced members of the audience. One of the most interesting
takeaways from the talk was the information about context aware access controls that
are going to be going in to general availability soon. The ability to write access
policies that are more or less restrictive based on the context of the access request
will be infinitely useful. One of the most interesting uses for my areas of
responsibility would be the ability to lock down ssh access to compute nodes based
on the on-call schedule. In general, we want to discourage artisanal modification
of our compute nodes, but sometimes it&amp;rsquo;s necessary for an engineer to do it in
emergency, on-call situations.&lt;/p&gt;
&lt;p&gt;The day wrapped up with some great socialization with my coworkers. I believe
that it&amp;rsquo;s vital to have a very strong relationship with your team members in
order for you to perform at your best. This is one of my favorite aspects of
conferences. We are all normally so wrapped up in our work that it can be
really hard to get to know your team during the normal work work.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all until tomorrow!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Google Next 2019: Day 1</title>
      <link>https://platfrastructure.life/post/next2019-01/</link>
      <pubDate>Mon, 08 Apr 2019 18:22:14 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/next2019-01/</guid>
      <description>&lt;p&gt;Today was a normal work day for me, but normal only in the sense that I was
doing work. The conference itself doesn&amp;rsquo;t actually start until tomorrow.
Mailchimp has a couple of remote offices and one of them is in Oakland, CA. I
came in on Sunday with the goal of spending Monday at our California office and
getting to know my California coworkers. To that end, I think the day has been
a success. I was able to have lunch with one of my Oakland teammates and I&amp;rsquo;m
hoping there will be a few people who want to grab dinner later today.&lt;/p&gt;
&lt;p&gt;I have high hopes for Google Next this year. Last year was good but I was still
very new to Google&amp;rsquo;s cloud products. Since last year, Mailchimp has started to do
more experimentation with their cloud products and services and I feel that I&amp;rsquo;ve
also grown a bit in my technical understanding in various areas. I&amp;rsquo;m hoping that
this will allow me to absorb more, understand more deeply, the topics that are
discussed.&lt;/p&gt;
&lt;p&gt;For now, I&amp;rsquo;m going to go find some food. I&amp;rsquo;ll be back tomorrow with a day two
report!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Google Next 2019: Day 0</title>
      <link>https://platfrastructure.life/post/next2019-00/</link>
      <pubDate>Sun, 07 Apr 2019 16:54:03 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/next2019-00/</guid>
      <description>&lt;p&gt;I landed in San Francisco, CA today for what is sure to be an interesting week.
This week is Google&amp;rsquo;s annual Google Cloud Next conference. I would be remiss if
I didn&amp;rsquo;t take the opportunity to enjoy a little personal time on the west coast.
As usual I brought my skates with me and will be enjoying the Sunday night skate
at Golden Gate Park as well as the Friday night skate before I head home.&lt;/p&gt;
&lt;p&gt;This year&amp;rsquo;s hotel is a bit of an experiment. I&amp;rsquo;m not staying at one of the large
chain hotels this year. I opted for a smaller independent hotel in China Town.
Of course, I got in super early so I haven&amp;rsquo;t actually seen my room yet but I
did get to have some good dim sum for lunch. So we&amp;rsquo;re off to a good start.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sports in my Life</title>
      <link>https://platfrastructure.life/post/sports/</link>
      <pubDate>Wed, 20 Mar 2019 17:21:45 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/sports/</guid>
      <description>&lt;p&gt;Little known fact about me, I like sports. It feels kind of risqué admitting it
to an audience no doubt made up of mostly technologists, geeks and nerds; the
very people with whom I regularly make &amp;ldquo;sports ball&amp;rdquo; jokes with, but it&amp;rsquo;s true.
But I&amp;rsquo;m not your typical sports fan. Sure, I watch the Superbowl. Yes, I enjoy
going to see a baseball game with friends once in a while (maybe &amp;lt; once a year
in reality) but those aren&amp;rsquo;t my passion. I like individual sports. My personal
favorites to watch are speed skating and cycling. Coincidentally, those also
happen to be two that I compete in. SURPRISE, I don&amp;rsquo;t sit in front of my
computer all day. In fact, of all the things I do in life, that&amp;rsquo;s probably my
least favorite.&lt;/p&gt;
&lt;p&gt;One of the earliest memories I have as a child is my parents teaching me to ride
a bike. Some years later, maybe when I was somewhere between 8-11 years old, I
discovered the world of inline skating. I&amp;rsquo;ve felt at home both on two and four
wheels ever since. It wasn&amp;rsquo;t until I was about 17 years old, however, that I
really found an interest in riding bikes (more than just as a way to get to a
friend&amp;rsquo;s house at least). I spent the junior and senior years of high school
doing basically two things outside of school: biking and discovering the
Internet. I would come home from school, immediately get on my bicycle and ride
for hours. I&amp;rsquo;d come home in time for family dinner, do my homework (maybe) and
then I was upstairs in my &amp;ldquo;cave&amp;rdquo;, online on IRC, message boards, you name it. I
would typically be up until 2, 3AM, sometimes later. Up for school the next day
at 6AM, repeat.&lt;/p&gt;
&lt;p&gt;It wasn&amp;rsquo;t until last year, however, that I thought that maybe I should try this
racing thing. I&amp;rsquo;d always just enjoyed skating or biking for their own right. I
never felt compelled to prove I was better than another person. Something in me
has changed in the last year. While I still don&amp;rsquo;t feel that competitive drive to
beat another person, I want to beat myself; to prove that I can consistently
improve on my own times and my own achievements. This has driven me to make
triathlon my sport of choice this year.&lt;/p&gt;
&lt;p&gt;I view triathlon as a sport in which you prove your capabilities only to
yourself. Sure you can be faster than another person, you can win the race. But
so many people compete not against others but against the clock , against
themselves in this sport. It feels like a natural fit. This year, I have no
real goals other than to be consistent. I&amp;rsquo;m on a structured training plan which
has already started to really help me improve. I also have a couple of races
lined up for the year, just something to motivate me to stay on target with my
training.&lt;/p&gt;
&lt;p&gt;So, I&amp;rsquo;m going to try to keep the tags straight on the posts but you might start
seeing some non-tech posts popping up every now and again. Hopefully you&amp;rsquo;ll
stick around for the journey.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Soma.fm and Mplayer</title>
      <link>https://platfrastructure.life/post/soma_shell/</link>
      <pubDate>Thu, 20 Dec 2018 14:14:08 -0500</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/soma_shell/</guid>
      <description>&lt;p&gt;I like to listen to &lt;a href=&#34;https://somafm.com&#34;&gt;soma.fm&lt;/a&gt; at work. I also like to do
everything (as much as possible) in the command line so I don&amp;rsquo;t have to context
switch between applications too much. For years I&amp;rsquo;ve used mplayer to listen in.
I eventually got tired of forgetting the playlist URL and wrote the following
bash function so that I&amp;rsquo;d only have to remember the name of the playlist I
wanted.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;soma() {
  mplayer -playlist http://somafm.com/&amp;#34;$1&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This worked well for a couple of years, really. I usually only listen to one or
two stations and can easily remember the name of the station&amp;rsquo;s playlist file.
Today I decided that I wanted to be able to explore new stations with this shell
function as well. Enter the new bash function and a bash completion script.&lt;/p&gt;
&lt;p&gt;The new bash function parses &lt;a href=&#34;https://somafm.com/listen&#34;&gt;https://somafm.com/listen&lt;/a&gt; for the MP3 streams and
prints them to STDOUT:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;somalist() {
  curl -s https://somafm.com/listen/ | awk -F &amp;#39;[&amp;lt;&amp;gt;]&amp;#39; &amp;#39;/MP3:/ { print $4 }&amp;#39; | awk -F &amp;#39;&amp;#34;&amp;#39; &amp;#39;{print $2}&amp;#39; | tr -d \/
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is not great but it works, for now. Parsing html with awk/bash/sed or any
other generic interpreter is always hit or miss. Right now this works. In the
future, maybe it won&amp;rsquo;t work so great. I&amp;rsquo;m prepared to accept this and fix it as
needed. Now that we have a command that can play our playlists and one that can
list them, we need tie it all together with bash completion.&lt;/p&gt;
&lt;p&gt;Bash completion scripts are what automatically tab complete options and
arguments. These can be static or dynamic. In our case, we want to provide the
output of &lt;code&gt;somalist()&lt;/code&gt; to the user when they type &lt;code&gt;soma&lt;/code&gt; and press the &amp;lt;tab&amp;gt; key.
On MacOS the following file should be placed in &lt;code&gt;/usr/local/etc/bash_completion.d&lt;/code&gt;
(assuming you&amp;rsquo;re using Homebrew). If you&amp;rsquo;re on Linux, it will likely be
&lt;code&gt;/etc/bash_completion.d&lt;/code&gt;. Consult your distribution specific documentation for
more details:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#!/usr/bin/env bash

_soma_completion() {
    if [ &amp;#34;${#COMP_WORDS[@]}&amp;#34; != &amp;#34;2&amp;#34; ]; then
        return
    fi

    COMPREPLY=($(compgen -W &amp;#34;$(somalist)&amp;#34; -- &amp;#34;${COMP_WORDS[1]}&amp;#34;))
}

complete -F _soma_completion soma
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Source this file with &lt;code&gt;source /usr/local/etc/bash_completion.d/soma&lt;/code&gt; and try it
out by typing &lt;code&gt;soma&lt;/code&gt; followed by the &lt;!-- raw HTML omitted --&gt; key. As an added bonus, it will give
you the most closely matching result if you type a partial name in.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;This works for me. It&amp;rsquo;s a little fragile but I found it useful enough that I&amp;rsquo;d
share it. Check out my Github if you&amp;rsquo;re interested in some of the other scripts
I whip up and updates to this if I decide to make improvements in the future.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Building GRPC on CentOS 6</title>
      <link>https://platfrastructure.life/post/grpc_build_centos6/</link>
      <pubDate>Fri, 02 Mar 2018 11:04:27 -0500</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/grpc_build_centos6/</guid>
      <description>&lt;p&gt;When transitioning from a monolithic application towards a more service oriented
architecture you&amp;rsquo;re likely to either have a period where you have a hybrid setup
until all services are split out of the monolith or make the conscious decision
to leave some things be either temporarily or for the long term. Does this mean
you should have to sacrifice consistency in your API? NO!&lt;/p&gt;
&lt;p&gt;In my case, I was dealing with servers as part of a monolithic application
running on CentOS 6. We chose to use GRPC for our services but it requires a
newer compiler than provided in the normal CentOS repos. This post will outline
my solution to the problem and hopefully help others who find themselves in
similar positions.&lt;/p&gt;
&lt;h1 id=&#34;the-compiler&#34;&gt;The Compiler&lt;/h1&gt;
&lt;p&gt;CentOS 6 was released about 7 years ago. July 10 2011 to be exact. That means
it&amp;rsquo;s Ancient in technology years. These stable OSes are great for running things
that you don&amp;rsquo;t plan on changing for a very, very long time but not so great for
developing software using the latest tools and libraries. Such is the case if
you want to use GRPC within your application to talk to some of your more modern
services. The main issue is that the compiler available in CentOS 6 isn&amp;rsquo;t C++11
compatible. As a result, the compile flags will not be recognized and it will
fail to build. The first thing we need to do then is find a C++11 compatible
compiler for CentOS 6.&lt;/p&gt;
&lt;h1 id=&#34;the-repository&#34;&gt;The Repository&lt;/h1&gt;
&lt;p&gt;After much searching I came across &lt;a href=&#34;https://people.centos.org/tru/devtools-2/&#34;&gt;a
repository&lt;/a&gt; on
&lt;a href=&#34;https://people.centos.org&#34;&gt;people.centos.org&lt;/a&gt; owned by Tru Huynh, a developer
with the CentOS project since 2005 and member of their infrastructure team. This
repository contains the devtools-2 packages which, coincidentally, include a
version of gcc which is C++11 compatible. To install this repository and the
compiler run the following:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtools-2.repo
yum install devtoolset-2-gcc devtoolset-2-binutils devtoolset-2-gcc-c++
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;using-the-new-compiler&#34;&gt;Using the New Compiler&lt;/h1&gt;
&lt;p&gt;Once you&amp;rsquo;ve installed the packages above you&amp;rsquo;ll need to be sure you&amp;rsquo;re in the
right environment. There are two ways to do this. The quick way to do it (if
you&amp;rsquo;re just testing) is to run:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;scl enable devtoolset-2 bash
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will drop you in to a shell with the right environment to use the new
compiler. If you&amp;rsquo;re doing this as part of a script or in a build server where
you&amp;rsquo;ll always want to use this compiler, set the following in your environment
(either through .bash_profile, .bashrc, or similar):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#34;WARNING: devtoolset-2 is enabled!&amp;#34;
source /opt/rh/devtoolset-2/enable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s it! You can now build GRPC (or wahtever you want) using this compiler and
it will work on CentOS 6.&lt;/p&gt;
&lt;h1 id=&#34;conclusion-and-notes&#34;&gt;Conclusion and Notes&lt;/h1&gt;
&lt;p&gt;One thing to note about this repository. It is not an official repo and the
packages within it are not signed. The SRPMS are published if you want to look
over the work yourself or make any modifications. I&amp;rsquo;d suggestion you review
these tools careful before you decide to use them yourself.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Repairing a Raspberry Pi Boot Partition</title>
      <link>https://platfrastructure.life/post/rpi_boot_repair/</link>
      <pubDate>Mon, 26 Feb 2018 18:08:28 -0500</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/rpi_boot_repair/</guid>
      <description>&lt;p&gt;The power at my home went out the other day. When it came back on, my ADSB
receiver running on a Raspberry Pi, however, did not. It would flash green for a
moment and then stop all activity. Plugging it up to a monitor confirmed my
fear, it booted to the dreaded rainbow screen and no further. This meant the
filesystem was likely corrupt. This post will walk you through my
troubleshooting steps and ultimate resolution of this issue.&lt;/p&gt;
&lt;h1 id=&#34;troubleshooting&#34;&gt;Troubleshooting&lt;/h1&gt;
&lt;p&gt;The first thing you want to do with a corrupted SD image is isert it in a
working environment and run &lt;code&gt;fsck&lt;/code&gt;. On a default Raspbian install there will be
two partitions on the SD card. One is formatted fat32, the other will likely be
ext4. The following steps assume you&amp;rsquo;re running linux and you have the SD card
inserted in to your computer. I&amp;rsquo;ll walk you through unmounting both partitions
but if you know how to do that, go ahead and do so now.&lt;/p&gt;
&lt;h2 id=&#34;unmount-the-sd-card&#34;&gt;Unmount the SD Card&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll first want to unmount the card (but leave the card inserted) to do this,
run the following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo umount /dev/mmcblkp*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once unmounted we can proceed to repairing the filesystem.&lt;/p&gt;
&lt;h2 id=&#34;repair-the-est4-partition&#34;&gt;Repair the est4 partition&lt;/h2&gt;
&lt;p&gt;Repairing the ext4 partition requires that we have e2fsck installed. Check your
package manager if you don&amp;rsquo;t have the command available.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;e2fsk -f -y /dev/mmcblkp2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once complete, try removing the SD card and booting the Raspberry Pi. If it
still won&amp;rsquo;t boot. Re-insert the card in your computer, unmount it like we did
earlier and proceed to the next section.&lt;/p&gt;
&lt;h2 id=&#34;repair-the-boot-partition&#34;&gt;Repair the boot partition&lt;/h2&gt;
&lt;p&gt;The boot partition is formatted in fat32. To repair this, we&amp;rsquo;ll use the dosfsck
program. Again, check your package manager to find out what package provides
this tool on your system if you don&amp;rsquo;t have the command available.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dosfsck -t -a -w /dev/mmcblkp1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make note of any files that this commands reports as &amp;ldquo;unreadable&amp;rdquo;. We&amp;rsquo;ll need to
replace these a little later on. If you don&amp;rsquo;t see this message, you should try
booting your card now. It should work. If it doesn&amp;rsquo;t you may need to replace the
entire filesystem. The steps for doing so will be similar to the ones in the
next section but you&amp;rsquo;ll replace every file instead of just the unreadable ones
you would have noted during the previous command.&lt;/p&gt;
&lt;h1 id=&#34;replace-corrupted-files-in-boot&#34;&gt;Replace Corrupted Files in /boot&lt;/h1&gt;
&lt;p&gt;The last repair step is replacing corrupted files in your /boot partition. If
you&amp;rsquo;re here, something went very wrong. Thankfully, repairing things isn&amp;rsquo;t as
bad as some might have you believe. We&amp;rsquo;ll need to download a copy of the
raspbian image file, mount it to our local system and copy the corrupted files
from the known good image on to our SD card.&lt;/p&gt;
&lt;h2 id=&#34;download-raspbian&#34;&gt;Download Raspbian&lt;/h2&gt;
&lt;p&gt;The first thing you need to do is download a copy of Raspbian if you don&amp;rsquo;t
already have a copy. You can download that
&lt;a href=&#34;https://www.raspberrypi.org/downloads/raspbian/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;mount-the-image&#34;&gt;Mount the Image&lt;/h2&gt;
&lt;p&gt;Mounting an img file is a lot like mounting an ISO file if you&amp;rsquo;ve ever done
that. The difference is that we need to provide the partition offset manually to
the mount command.&lt;/p&gt;
&lt;p&gt;First, we create a path to mount the img file to.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkdir /tmp/raspbian
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we need to get some info about the file from fdisk. Replace
&lt;code&gt;&amp;lt;raspbian_image_path&amp;gt;&lt;/code&gt; below with the actual path to your img file.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fdisk -l &amp;lt;raspbian_image_path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should see an output that looks similar to this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ fdisk -l 2017-11-29-raspbian-stretch-lite.img
Disk 2017-11-29-raspbian-stretch-lite.img: 1.7 GiB, 1858076672 bytes, 3629056 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x37665771

Device                                Boot Start     End Sectors  Size Id Type
2017-11-29-raspbian-stretch-lite.img1       8192   93236   85045 41.5M  c W95 FAT32 (LBA)
2017-11-29-raspbian-stretch-lite.img2      94208 3629055 3534848  1.7G 83 Linux
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You want to multiply the Sector size by the start-block for img1. In the example
above this would be &lt;code&gt;512 * 8192 = 4194304&lt;/code&gt;. 4194304 is our offset. Now we&amp;rsquo;ll use
that to mount the img file to &lt;code&gt;/tmp/raspbian&lt;/code&gt;. Once again, replacing
&lt;code&gt;&amp;lt;path_to_image&amp;gt;&lt;/code&gt; with our actual file path.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mount -o loop,offset=4194304 &amp;lt;path_to_image&amp;gt; /tmp/raspbian
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should now be able to list the contents of the boot partition in the image
file with &lt;code&gt;ls /tmp/raspbian&lt;/code&gt;. Now insert your SD card and copy the files from
the mounted image to the /boot partition on your SD card. Once you&amp;rsquo;re done,
unmount the SD card and attempt to boot the Pi again.&lt;/p&gt;
&lt;p&gt;One thing I noticed when copying the files over was that one of the dtb files
was missing from the image. I just skipped over this and replaced what I could.
For me, this was fine. YMMV.&lt;/p&gt;
&lt;p&gt;Hopefully this helps anyone stuck with a corrupt SD card image get back up and
running again. I imagine this same method can be used to repair the ext4
partition as well. Just adjust your offset calculation to use the start-block
for the img2 partition instead in order to access the files.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gophercon Day 3</title>
      <link>https://platfrastructure.life/post/gophercon_day3/</link>
      <pubDate>Sun, 16 Jul 2017 08:28:05 -0600</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/gophercon_day3/</guid>
      <description>&lt;p&gt;Yes, you&amp;rsquo;re not lost. I skipped day 2. Not physically but for the purposes of
this blog I&amp;rsquo;ll be skipping it. Why you ask? It&amp;rsquo;s simple, I am a day late
updating this thing and nothing memorable must have happened because I don&amp;rsquo;t
remember anything specific about the day. The talks were good; I was able to
make it in to the ones I wanted this time. I won a plushie Gopher doll at the
Google booth.  But otherwise, uneventful.&lt;/p&gt;
&lt;p&gt;Day three, however, was community day, and my experience was extremely positive
and memorable. Community day for me turned in to a Go hack-a-thon. I met up
with &lt;a href=&#34;https://twitter.com/Kris__Nova&#34;&gt;Kris Nova&lt;/a&gt; and others to work on Kris&#39;
new project &lt;a href=&#34;https://github.com/kris-nova/kubicorn&#34;&gt;Kubicorn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Kubicorn is a project that helps a user manage cloud infrastructure for
Kubernetes.  With kubicorn a user can create new clusters, modify and scale
them, and take a snapshot of their cluster at any time. In other words, it&amp;rsquo;s a
simplified re-thinking of how we define, deploy and backup our Kubernetes
cluster infrastructure. Rather than using JSON first, we define profiles as
struct literals and marshal in to an object. Striving for clean code and an
empathetic user experience.&lt;/p&gt;
&lt;p&gt;This was my first experience working on a real project with Go. Getting in on a
project at such an early stage was a unique experience I was not well prepared
for. I am still very new to Go and have only a little experience contributing
actual code to a software project. Being able to work with other developers in
person as a neophyte was a huge boost. Normally I would not have had the
confidence to contribute to anything as technical as this project but having
other experienced developers nearby to assist with technical and design
questions allowed me to produce several pull requests against the project, as
minor as those PRs may have been.&lt;/p&gt;
&lt;p&gt;As Gophercon comes to a close, I want to give a huge thank you to the
conference organizers. This conference was the best one I&amp;rsquo;ve attended this
year. It&amp;rsquo;s still growing and with that comes some struggles and the GopherCon
team did a very good job of making the conference an experience to remember. I
will definitely look forward to being able to attend the conference again in the
future. Unfortunately, not next year (the dates conflict with another recurring
conference I have attended for over 10 years) but perhaps in 2019.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>GopherCon Day 1</title>
      <link>https://platfrastructure.life/post/gophercon_day1/</link>
      <pubDate>Fri, 14 Jul 2017 15:04:47 -0600</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/gophercon_day1/</guid>
      <description>&lt;p&gt;GopherCon 2017 day 1 has concluded. From my perspective, it was a good day. The
talks were good. The hallway track was, as always, great. The food, fantastic.
The crowds, overwhelming.&lt;/p&gt;
&lt;p&gt;The day started off with the keynote by &lt;a href=&#34;https://twitter.com/tammybutow&#34;&gt;Tammy Butow&lt;/a&gt;.
From there, &lt;a href=&#34;https://twitter.com/_rsc&#34;&gt;Russ Cox&lt;/a&gt; provided a look at the future of
the Go language. After the break for coffee the day resumed with an attempt to
see &lt;a href=&#34;https://twitter.com/lizrice&#34;&gt;Liz Rice&amp;rsquo;s&lt;/a&gt; talk on Syscalls. I say an atempt
was made because the crowds at GopherCon this year were unexpected both by me
and the GopherCon team. Popular track rooms quickly filled and many people were
unable to attend. This trend continued throughout the day. Thankfully, with better
planning, the afternoon tracks went better.&lt;/p&gt;
&lt;p&gt;At 2PM &lt;a href=&#34;https://twitter.com/kelseyhightower&#34;&gt;Kelsey Hightower&lt;/a&gt; spoke about the
possibilities of self deploying Go applications on Kubernetes. This talk was
informative and, as alwys, entertaining. I question the practicality of his
proposals in production but the idea of an application being able to self
deploy to a Kubernetes cluster is definitely worth further exploration.&lt;/p&gt;
&lt;p&gt;That concluded the talks I attended for the day. There was another failed attempt
in the afternoon to attend the talk on Go Anti-Patterns, again due to over crowding.&lt;/p&gt;
&lt;p&gt;I look forward to the rest of the week.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>GopherCon Day 0</title>
      <link>https://platfrastructure.life/post/gophercon_day0/</link>
      <pubDate>Wed, 12 Jul 2017 09:23:12 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/gophercon_day0/</guid>
      <description>&lt;p&gt;I landed in Denver, CO yesterday for &lt;a href=&#34;https://www.gophercon.com/&#34;&gt;GopherCon 2017&lt;/a&gt;.
I&amp;rsquo;ve never been to Denver before. I didn&amp;rsquo;t know what to expect upon landing. I
had heard things, of course. Mostly through what I hear in the media. So, naturally,
the only thing I knew was that Marjuana was legal and widely available through
shops all over the state. What I wasn&amp;rsquo;t expecting, however, was what I saw upon
approach to land at the airport. Miles and miles of flat farm land with a beautiful
backdrop of snow capped mountains. The second thing that struck me was how laid
back the airport atmosphere was. It&amp;rsquo;s quite possibly the least busy airport I&amp;rsquo;ve
been to in recent memory. The final thing that struck me which I&amp;rsquo;ll mention here
is the city. Denver is one of the most beautiful cities in the I&amp;rsquo;ve yet visited
in the United States. I was fortunate to have time after dinner to go for a walk
and look around. The city is clean. The people are friendly and the architecture
of the buildings around the capital is gorgeous, rivaled only by those I&amp;rsquo;ve seen
in Washington, DC.&lt;/p&gt;
&lt;p&gt;If the first night in town is any indication of what&amp;rsquo;s to come, I look forward to
the rest of my stay with great excitement.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Automating the Site with Hugo and Terraform</title>
      <link>https://platfrastructure.life/post/hugo_terraform/</link>
      <pubDate>Tue, 23 May 2017 23:09:14 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/hugo_terraform/</guid>
      <description>&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;d like to start with a some background. This site is hosted on &lt;a href=&#34;digitalocean.com&#34;&gt;Digital
Ocean&lt;/a&gt;. This site is built using &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt;.
I deploy this site using &lt;a href=&#34;https://www.terraform.io/&#34;&gt;Terraform&lt;/a&gt;. It runs on the
lowest end $5/month droplet from Digital Ocean.&lt;/p&gt;
&lt;p&gt;If This sounds appealing to you, read further. We&amp;rsquo;ll be discussing how you too
can have a blog site without the need for heavy CMS software like Wordpress,
etc.&lt;/p&gt;
&lt;h1 id=&#34;summary&#34;&gt;Summary&lt;/h1&gt;
&lt;p&gt;In this post we&amp;rsquo;ll work through the process of running a simple blogging site
using Hugo. We will also discuss one possible way (the way I am using) to
deploy the site using Terraform via the Digital Ocean provider.&lt;/p&gt;
&lt;p&gt;All code for this website can be found on the project page located on
&lt;a href=&#34;https://github.com/k4k/xy0_org&#34;&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;terraform&#34;&gt;Terraform&lt;/h1&gt;
&lt;p&gt;Terraform is an automated server deployment utility. You define how to
construct your server inside of a configuration file (or multiple configuration
files in the same directory). It has
&lt;a href=&#34;https://www.terraform.io/docs/providers/&#34;&gt;providers&lt;/a&gt; for many different cloud
service providers. For the purposes of this blog post I&amp;rsquo;ll be focusing on the
provider for &lt;a href=&#34;https://www.terraform.io/docs/providers/do/index.html&#34;&gt;Digital
Ocean&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;installing-terraform&#34;&gt;Installing Terraform&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.terraform.io/intro/getting-started/install.html&#34;&gt;Installing
Terraform&lt;/a&gt; is
fairly straight forward. On most platforms it&amp;rsquo;s a matter of downloading the
platform specific zip/tar file and extracting the binary. This binary can then
be placed anywhere you like. I recommend with $HOME/bin or /usr/loca/bin for
convenience and consistency.&lt;/p&gt;
&lt;h2 id=&#34;terraform-files&#34;&gt;Terraform Files&lt;/h2&gt;
&lt;p&gt;Defining your system is done in files with a .tf extension. For this site, I
have split the files up in to two different .tf files. The first file,
webserver.tf, is where the bulk of the configuration occurs. I have two main
sections within this file. The first section describes the DNS resource. This
uses the Digital Ocean provider to dynamically create a DNS A record for the
site once everything is setup and ready to go. The second section defines the
actual droplet. What image to use, droplet size, how to remotely connect and
what to do to configure the server.&lt;/p&gt;
&lt;p&gt;The second file, provider.tf, contains the provider specific information. This
includes Digital Ocean API keys, private and public key file names and other
various information which I would not want made public to the world. I am a
firm believer in version controlling everything possible. By splitting up the
sensitive information I am able to put the bulk of the config on GitHub and add
an entry in to the .gitignore file for the provider.tf, avoiding any
possibility of accidentally uploading sensitive information to a public
repository.&lt;/p&gt;
&lt;p&gt;You can find the Terraform file that deploys this website can be found
&lt;a href=&#34;https://github.com/k4k/xy0_org/blob/master/terraform-files/webserver.tf&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;hugo&#34;&gt;Hugo&lt;/h1&gt;
&lt;p&gt;Hugo is a static website generator written in &lt;a href=&#34;https://golang.org/&#34;&gt;Go&lt;/a&gt;, a
powerful and modern programming language. With Hugo, all of the page content is
written in &lt;a href=&#34;https://daringfireball.net/projects/markdown&#34;&gt;Markdown&lt;/a&gt;. The layout
of the page is defined by it&amp;rsquo;s &lt;a href=&#34;https://gohugo.io/content/types/&#34;&gt;type&lt;/a&gt;. The
configuration files can be defined in TOML, YAML or JSON.&lt;/p&gt;
&lt;h2 id=&#34;installing-hugo&#34;&gt;Installing Hugo&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://gohugo.io/overview/installing/&#34;&gt;Installing Hugo&lt;/a&gt; is slightly more
complicated than Terraform, but still fairly simple. If you are on MacOS I
recommend installing with &lt;a href=&#34;https://brew.sh/&#34;&gt;Homebrew&lt;/a&gt;. All other platforms
should install using the platform specific instructions provided on the install
page.&lt;/p&gt;
&lt;h2 id=&#34;build-your-first-hugo-site&#34;&gt;Build your first Hugo site&lt;/h2&gt;
&lt;p&gt;Once installed, creating the skeleton for your first site is as simple as
running &lt;code&gt;hugo new site &amp;lt;name_of_site&amp;gt;&lt;/code&gt;. Change directories in to your site and
edit the config.toml file. You can find the config toml used for this site
&lt;a href=&#34;https://github.com/k4k/xy0_org/blob/master/site-files/config.toml&#34;&gt;here&lt;/a&gt;. This
config file defines the value of things such as your site name and theme as
well as the &lt;a href=&#34;https://gohugo.io/extras/menus/&#34;&gt;menu&lt;/a&gt; for your site.&lt;/p&gt;
&lt;p&gt;Once you have some of the config.toml figured out you should select a theme.
Right out of the box, your site doesn&amp;rsquo;t know how to serve up the content files
we will be adding. Adding a basic theme from the &lt;a href=&#34;http://themes.gohugo.io/&#34;&gt;gohugo
themes&lt;/a&gt; site is a quick way to get your content up.
If you want to do so later on there is great
&lt;a href=&#34;https://gohugo.io/tutorials/creating-a-new-theme/&#34;&gt;documentation&lt;/a&gt; on making
your own themes. For this tutorial, we&amp;rsquo;ll use the
&lt;a href=&#34;http://themes.gohugo.io/theme/after-dark/&#34;&gt;after-dark&lt;/a&gt; theme that I&amp;rsquo;m using here.&lt;/p&gt;
&lt;p&gt;After extracting the contents of the downloaded theme file to the themes
directory, we can return to the project root and run &lt;code&gt;hugo new post/first.md&lt;/code&gt;.
This will create a new post for us under content/post named &amp;ldquo;first.md&amp;rdquo;. This
file is already populated with what is called &amp;ldquo;front matter&amp;rdquo;. Front matter is
meta data about the page. It is defined using the archetypes found in the
&amp;ldquo;archetypes&amp;rdquo; folder. If you now, from the project root, run &lt;code&gt;hugo serve&lt;/code&gt; you
will start a web server on your system and should be able to browse the site by
visiting http://localhost:1313.&lt;/p&gt;
&lt;p&gt;But wait, there&amp;rsquo;s no content! Open content/post/first.md in your favorite text
editor and edit the &amp;ldquo;draft = true&amp;rdquo; to say &amp;ldquo;draft = false&amp;rdquo; instead.
Altneratively you can run the &lt;code&gt;hugo serve&lt;/code&gt; command with the -D flag to render
pages marked as drafts.&lt;/p&gt;
&lt;p&gt;If the above worked, the page you now see tells you a little about configuring
archetypes for the after-dark theme. Follow the instructions and edit the post
archetype to remove that message but leave the front matter in place.&lt;/p&gt;
&lt;h1 id=&#34;putting-the-two-together&#34;&gt;Putting the Two Together&lt;/h1&gt;
&lt;p&gt;Putting these two together requires just a little bit of glue but it&amp;rsquo;s more than
I care to explain in a blog post so I&amp;rsquo;ve made my entire setup &lt;a href=&#34;https://github.com/k4k/xy0_org&#34;&gt;publicly available
on Github&lt;/a&gt;. Most of it is documented and the scripts
make getting things setup fairly simple. It should be sufficiently detailed to
start you down the road of deploying your hugo site using terraform, at least.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;While I&amp;rsquo;ve used other deployment and configuration utilities in the past. This
is my first experience with Terraform. Overall, my impressions are positive.
It&amp;rsquo;s a good, general purpose, cloud deployment tool which makes getting started
down the path of &lt;a href=&#34;https://en.wikipedia.org/wiki/Infrastructure_as_Code&#34;&gt;infrastructure as
code&lt;/a&gt; easy and relatively
painless.&lt;/p&gt;
&lt;p&gt;As you continue to expand upon the topics discussed here and run your site long
term, you will likely find that configuring specific aspects of your server is
tedious with Terraform. Shell scripts can only get you so far. I would point
anyone interested in learning more towards some of the industry standard
configuration management tools available today for free.
&lt;a href=&#34;https://www.ansible.com/&#34;&gt;Ansible&lt;/a&gt; and &lt;a href=&#34;https://www.ansible.com/&#34;&gt;SaltStack&lt;/a&gt;
are simple, popular options that are well suited for both small and large scale
use. If you find yourself needing more than either of those can provide,
&lt;a href=&#34;https://puppet.com/&#34;&gt;Puppet&lt;/a&gt; is another good option.  You can also use Puppet
for smaller scale deployments but some features are restricted without a
complete puppet master/client setup.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>first</title>
      <link>https://platfrastructure.life/post/first/</link>
      <pubDate>Tue, 23 May 2017 22:03:36 -0400</pubDate>
      <author>platfrastructure@xy0.org (Ted Wood)</author>
      <guid>https://platfrastructure.life/post/first/</guid>
      <description>&lt;p&gt;This is my first hugo post. There will be a post to follow about how I built it.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
