<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Homelab on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/</link><description>Recent content in Homelab on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 05 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/homelab/index.xml" rel="self" type="application/rss+xml"/><item><title>BIFROST — Raspberry Pi jump node</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/bifrost/</link><pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/bifrost/</guid><description>&lt;p&gt;The homelab needed a permanent always-on entry point — something low power, always reachable, a stable first hop. A first-gen Raspberry Pi in the rack fills that role. BIFROST.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hardware"&gt;Hardware
&lt;/h2&gt;&lt;p&gt;Raspberry Pi 1 Model B running Raspbian, mounted in the rack with a &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rack-3d-prints/" &gt;3D printed 1U mount&lt;/a&gt;. Draws under 2W at idle. Nothing runs on it except sshd.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-it-works"&gt;How it works
&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;ssh -p 22222 user@bifrost.mjnet.info
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The chain:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;bifrost.mjnet.info&lt;/code&gt; — Route53 CNAME pointing to &lt;code&gt;router.mjnet.info&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;router.mjnet.info&lt;/code&gt; — HEIMDAL (SYS-009), kept current via DDNS&lt;/li&gt;
&lt;li&gt;OPNsense port forward: external TCP 22222 → Pi:22&lt;/li&gt;
&lt;li&gt;OPNsense DNS override: &lt;code&gt;bifrost.mjnet.info&lt;/code&gt; → Pi&amp;rsquo;s internal IP&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The DNS override means the same hostname resolves to the internal IP when used inside the network — no split config needed in &lt;code&gt;~/.ssh/config&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="opnsense-config"&gt;OPNsense config
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Port forward&lt;/strong&gt; (Firewall → NAT → Port Forward):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interface: WAN&lt;/li&gt;
&lt;li&gt;Protocol: TCP&lt;/li&gt;
&lt;li&gt;Destination port: 22222&lt;/li&gt;
&lt;li&gt;Redirect target: Pi internal IP, port 22&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;DNS override&lt;/strong&gt; (Services → Unbound DNS → Host Overrides):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Host: &lt;code&gt;bifrost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Domain: &lt;code&gt;mjnet.info&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;IP: Pi internal IP&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="ssh-config"&gt;SSH config
&lt;/h2&gt;&lt;p&gt;Add to &lt;code&gt;~/.ssh/config&lt;/code&gt; on any client:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Host bifrost
 HostName bifrost.mjnet.info
 Port 22222
 User pi
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then &lt;code&gt;ssh bifrost&lt;/code&gt; from anywhere.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If a more robust solution becomes necessary later (no open ports, survives CGNAT), the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/bifrost-rpi-options/" &gt;options doc&lt;/a&gt; covers Tailscale, Cloudflare Tunnel, and WireGuard.&lt;/p&gt;</description></item><item><title>Rack Support Brace — Programmatic 3D Print</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/</guid><description>&lt;p&gt;220×150×8mm support brace for the server rack. 40 through-holes in an alternating-spacing grid, B.E.S.T engraved into the top face, exported to STL — all generated by a Python script running in Blender. No manual modelling.&lt;/p&gt;




&lt;div class="sc-carousel" id="carousel-0"&gt;
 &lt;div class="sc-track"&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_front.png" loading="eager" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_front.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_iso.png" loading="lazy" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_iso.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_side.png" loading="lazy" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_side.png"&gt;
 &lt;/div&gt;
 
 &lt;div class="sc-slide"&gt;
 &lt;img src="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_top.png" loading="lazy" alt="/code/procedural-mesh/rack_support_brace/renders/rack_support_brace_220x150_top.png"&gt;
 &lt;/div&gt;
 
 &lt;/div&gt;
 
 &lt;button class="sc-btn sc-prev" aria-label="Previous"&gt;&amp;#8249;&lt;/button&gt;
 &lt;button class="sc-btn sc-next" aria-label="Next"&gt;&amp;#8250;&lt;/button&gt;
 &lt;div class="sc-dots"&gt;
 
 &lt;span class="sc-dot active"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;span class="sc-dot"&gt;&lt;/span&gt;
 
 &lt;/div&gt;
 
&lt;/div&gt;

&lt;style&gt;
.sc-carousel{position:relative;overflow:hidden;background:#0d0d0d;border-radius:8px;margin:1.5rem 0;user-select:none}
.sc-track{display:flex;transition:transform .4s ease}
.sc-slide{min-width:100%;display:flex;align-items:center;justify-content:center}
.sc-slide img{max-width:100%;max-height:560px;object-fit:contain}
.sc-btn{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,.55);color:#fff;border:none;font-size:2.4rem;line-height:1;padding:.05rem .65rem .15rem;cursor:pointer;border-radius:4px;z-index:10;transition:background .2s}
.sc-btn:hover{background:rgba(0,0,0,.85)}
.sc-prev{left:.5rem}.sc-next{right:.5rem}
.sc-dots{position:absolute;bottom:.65rem;left:0;right:0;display:flex;justify-content:center;gap:6px}
.sc-dot{width:8px;height:8px;background:rgba(255,255,255,.35);border-radius:50%;cursor:pointer;transition:background .2s}
.sc-dot.active{background:#fff}
&lt;/style&gt;

&lt;script&gt;
(function(){
 var root=document.getElementById('carousel-0');
 if(!root)return;
 var track=root.querySelector('.sc-track');
 var slides=root.querySelectorAll('.sc-slide');
 var dots=root.querySelectorAll('.sc-dot');
 var cur=0,timer;
 function go(n){
 if(dots[cur])dots[cur].classList.remove('active');
 cur=((n%slides.length)+slides.length)%slides.length;
 if(dots[cur])dots[cur].classList.add('active');
 track.style.transform='translateX(-'+cur*100+'%)';
 }
 var prev=root.querySelector('.sc-prev');
 var next=root.querySelector('.sc-next');
 if(prev)prev.addEventListener('click',function(){clearInterval(timer);go(cur-1);start()});
 if(next)next.addEventListener('click',function(){clearInterval(timer);go(cur+1);start()});
 dots.forEach(function(d,i){d.addEventListener('click',function(){clearInterval(timer);go(i);start()})});
 function start(){timer=setInterval(function(){go(cur+1)},"2800");}
 start();
})();
&lt;/script&gt;

&lt;p&gt;The same script that builds the geometry now also sets up cameras, runs EEVEE renders from four angles, and saves PNGs alongside the STL. The renders above are the direct output.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="downloads"&gt;Downloads
&lt;/h2&gt;&lt;p&gt;


&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/rack_support_brace.py" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;rack_support_brace.py
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;




&lt;a href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/rack_support_brace_220x150.stl" download class="dl-link"&gt;
 &lt;span class="dl-icon"&gt;↓&lt;/span&gt;rack_support_brace_220x150.stl
&lt;/a&gt;

&lt;style&gt;
.dl-link{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .75rem;border:1px solid currentColor;border-radius:4px;font-family:monospace;font-size:.875rem;text-decoration:none;opacity:.85;transition:opacity .15s}
.dl-link:hover{opacity:1;text-decoration:none}
.dl-icon{font-size:1rem;line-height:1}
&lt;/style&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-script"&gt;The script
&lt;/h2&gt;&lt;p&gt;Flip &lt;code&gt;EXPORT_STL&lt;/code&gt; / &lt;code&gt;EXPORT_RENDER&lt;/code&gt; at the top of the file, point &lt;code&gt;EXPORT_DIR&lt;/code&gt; at your folder, press &lt;code&gt;Alt+P&lt;/code&gt; in the Blender Script Editor.&lt;/p&gt;
&lt;p&gt;The config block at the top of the file is the only place dimensions live:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;PLATE_X = 220.0 # mm
PLATE_Y = 150.0 # mm
HOLE_DIAM = 7.0 # mm
N_COLS = 10
ROW_SPACING = 20.0 # mm
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Iterating means editing a constant and re-running — not touching a model.&lt;/p&gt;
&lt;p&gt;See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/" &gt;Blender Python for 3D Printing&lt;/a&gt; for the general pattern behind this.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-1--render-from-script-"&gt;Step 1 — Render from script ✓
&lt;/h2&gt;&lt;p&gt;Done. Script builds geometry → exports STL → places cameras at four angles (top, front, side, iso) → EEVEE renders → PNGs saved to &lt;code&gt;EXPORT_DIR/renders/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pipeline next steps (headless render, CI/CD, AI feedback loop) are tracked in the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/projects/procedural-mesh/" &gt;Procedural Mesh project&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-printed-part"&gt;The Printed Part
&lt;/h2&gt;&lt;figure&gt;&lt;img src="https://backend-engineering-strategy-tools.github.io/site/images/rack-brace.jpeg"
 alt="Finished rack support brace installed in the rack."&gt;&lt;figcaption&gt;
 &lt;p&gt;Finished rack support brace installed in the rack.&lt;/p&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;
</description></item><item><title>ASGARD — the blade cluster</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/asgard-blades/</link><pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/asgard-blades/</guid><description>&lt;p&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/inventory/systems/" &gt;ASGARD (SYS-007)&lt;/a&gt; is the HP BladeSystem C7000 with 16× BL460c Gen8 blades. The reason to use it is profile switching: boot a blade as a Slurm compute node, run the experiment, reimage it as a Talos worker, run the next one. The same iPXE boot menu already set up for &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/talos-omni/" &gt;ODEN&lt;/a&gt; works here — the C7000 Onboard Administrator lets you configure boot order per blade slot, so switching roles is a BIOS setting and a PXE entry, not a reinstall.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="power-reality"&gt;Power reality
&lt;/h2&gt;&lt;p&gt;Before committing to blades as the permanent always-on platform, it&amp;rsquo;s worth being honest about the enclosure overhead. The C7000 has fixed costs regardless of how many blades are populated: 10 fans, dual OA modules, 2 interconnect switches, backplane management. It doesn&amp;rsquo;t scale down gracefully.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Setup&lt;/th&gt;
 &lt;th&gt;Approx power&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;C7000 enclosure alone (no blades)&lt;/td&gt;
 &lt;td&gt;200–400W&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;C7000 + 1 blade&lt;/td&gt;
 &lt;td&gt;350–550W&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;C7000 + 3 blades&lt;/td&gt;
 &lt;td&gt;500–800W&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ODEN alone (1U M3, Talos)&lt;/td&gt;
 &lt;td&gt;100–150W&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;HEIMDAL alone (Sun X4150, router)&lt;/td&gt;
 &lt;td&gt;150–200W&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ODEN + HEIMDAL&lt;/td&gt;
 &lt;td&gt;250–350W&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Two pizza boxes beat three blades in the enclosure on power. The overhead only amortises at 8+ populated slots. For a permanent minimal setup, the 1U rack servers win. For experiments where you want to run 8–16 nodes at once, ASGARD earns its place.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-each-role-actually-needs"&gt;What each role actually needs
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Role&lt;/th&gt;
 &lt;th&gt;RAM&lt;/th&gt;
 &lt;th&gt;Disk&lt;/th&gt;
 &lt;th&gt;Network&lt;/th&gt;
 &lt;th&gt;Limiting factor&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Talos / K8s worker&lt;/td&gt;
 &lt;td&gt;32–64GB&lt;/td&gt;
 &lt;td&gt;1× OSD disk&lt;/td&gt;
 &lt;td&gt;1GbE fine&lt;/td&gt;
 &lt;td&gt;RAM — current blades too thin&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenStack compute&lt;/td&gt;
 &lt;td&gt;32–64GB&lt;/td&gt;
 &lt;td&gt;local ephemeral&lt;/td&gt;
 &lt;td&gt;1GbE fine&lt;/td&gt;
 &lt;td&gt;RAM&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenStack control&lt;/td&gt;
 &lt;td&gt;32GB+&lt;/td&gt;
 &lt;td&gt;small&lt;/td&gt;
 &lt;td&gt;1GbE fine&lt;/td&gt;
 &lt;td&gt;RAM&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Slurm compute&lt;/td&gt;
 &lt;td&gt;as much as possible&lt;/td&gt;
 &lt;td&gt;fast scratch&lt;/td&gt;
 &lt;td&gt;1GbE mediocre&lt;/td&gt;
 &lt;td&gt;network&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Ceph OSD&lt;/td&gt;
 &lt;td&gt;16–32GB&lt;/td&gt;
 &lt;td&gt;more / bigger disks&lt;/td&gt;
 &lt;td&gt;1GbE&lt;/td&gt;
 &lt;td&gt;disk count&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The network note matters for Slurm: blade LOM connects to the enclosure switch backplane at &lt;strong&gt;1GbE&lt;/strong&gt;, not 10GbE. The switch has 10GbE uplinks going out, but blade-to-blade traffic inside the enclosure goes through the switch at 1GbE. For Talos and OpenStack this is fine. For MPI jobs exchanging large datasets between Slurm nodes it&amp;rsquo;s a real bottleneck — HPC wants InfiniBand, which the empty interconnect bays 5–8 could take (plus matching mezzanine cards in each blade), but that&amp;rsquo;s a separate cost. For learning Slurm, 1GbE is workable.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="current-blade-state"&gt;Current blade state
&lt;/h2&gt;&lt;p&gt;Most blades are underpowered for any of the roles above. CPUs are also unknown across all 16 slots — the OA web GUI reports CPU model and core count per blade and should be checked first. The E5-2600 v1 range runs from E5-2603 (4c, 80W) to E5-2690 (8c/16t, 135W), which matters significantly for role assignment.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Slot&lt;/th&gt;
 &lt;th&gt;RAM&lt;/th&gt;
 &lt;th&gt;Disk&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-001&lt;/td&gt;
 &lt;td&gt;4GB&lt;/td&gt;
 &lt;td&gt;2× 146GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-002&lt;/td&gt;
 &lt;td&gt;14GB (mixed, odd count)&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-003&lt;/td&gt;
 &lt;td&gt;32GB&lt;/td&gt;
 &lt;td&gt;2× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-004&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-005&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;1× 146GB + 1× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-006&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;2× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-007&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;2× 900GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-008&lt;/td&gt;
 &lt;td&gt;16GB&lt;/td&gt;
 &lt;td&gt;2× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-009&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-010&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;2× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-011&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;2× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-012&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;2× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-013&lt;/td&gt;
 &lt;td&gt;32GB&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-014&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-015&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;2× 300GB SAS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLD-016&lt;/td&gt;
 &lt;td&gt;8GB&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;BLD-003 and BLD-013 are already at 32GB and are natural candidates for control-plane or master roles once CPUs are confirmed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="suggested-configuration-from-existing-stock"&gt;Suggested configuration from existing stock
&lt;/h2&gt;&lt;p&gt;Available spare hardware:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;14× RAM-007 (8GB DDR3 1600MHz ECC Reg) — unassigned&lt;/li&gt;
&lt;li&gt;2× HDD-004 (120GB SATA SSD) — spare&lt;/li&gt;
&lt;li&gt;6× HDD-002 (146GB 10K SAS) — spare&lt;/li&gt;
&lt;li&gt;Embedded P220i on each blade (can be set to JBOD/passthrough for Ceph)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Fat&amp;rdquo; nodes × 2&lt;/strong&gt; — Talos control plane, OpenStack control, Slurm master:
Add 4× RAM-007 to each blade. From a base of 8–16GB that gives ~40GB. Candidates: BLD-006 and BLD-010, both have 2× 300GB SAS for local storage. Costs 8 of 14 spare sticks. Install a spare 120GB SSD as boot disk in each.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Medium&amp;rdquo; nodes × 3&lt;/strong&gt; — Talos workers, OpenStack compute, Slurm compute:
Add 2× RAM-007 to each → 24GB from the 8GB base. Candidates: BLD-008 (already 16GB, gets to 32GB), BLD-011, BLD-012. All three have 300GB SAS for scratch or Ceph OSDs. Costs the remaining 6 spare sticks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rest&lt;/strong&gt; — thin compute, storage expansion, or powered off:
Leave at current RAM. BLD-007&amp;rsquo;s 900GB SAS pair is better used elsewhere (see below). BLD-003 and BLD-013 at 32GB can step up to fat-node role once CPUs are confirmed.&lt;/p&gt;
&lt;p&gt;That leaves 5 blades properly kitted and 11 available for experiments or idle.&lt;/p&gt;
&lt;p&gt;BL460c Gen8 DIMM rule: populate per-CPU symmetrically — pairs or quads per memory channel — for best throughput. Don&amp;rsquo;t mix odd counts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="storage--what-moves-where"&gt;Storage — what moves where
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Pull the 900GB SAS drives from BLD-007 now.&lt;/strong&gt; HDD-013 (HGST 900GB) and HDD-014 (Toshiba 900GB) are the two largest drives in the blade pool and they&amp;rsquo;re sitting in a blade that may end up as a thin compute worker. Move them into ODEN or LOKE as permanent Ceph OSDs. This immediately gives the always-on cluster substantially more storage than the current 120GB SSDs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MIMIR&lt;/strong&gt; (SYS-004, 15× 1TB SAS) is the Ceph expansion story for later. To connect it: install CTRL-006 (ServeRAID-8e, have 2 unplaced) into a server with a free PCIe slot, then cable it with a SFF-8470 → SFF-8088 cable (not currently owned, inexpensive). TOR is the natural host — it already has CTRL-003 in HBA mode and free PCIe slots. Not urgent, but the hardware is almost all there.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;What&lt;/th&gt;
 &lt;th&gt;Goes to&lt;/th&gt;
 &lt;th&gt;When&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;900GB SAS ×2 from BLD-007&lt;/td&gt;
 &lt;td&gt;ODEN or LOKE, permanent Ceph OSDs&lt;/td&gt;
 &lt;td&gt;Now&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;120GB SSD ×2 spare&lt;/td&gt;
 &lt;td&gt;BLD fat node boot disks&lt;/td&gt;
 &lt;td&gt;Before Talos on blades&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;300GB SAS in blades&lt;/td&gt;
 &lt;td&gt;Local scratch or blade Ceph OSDs&lt;/td&gt;
 &lt;td&gt;During ASGARD experiments&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;MIMIR 15× 1TB SAS&lt;/td&gt;
 &lt;td&gt;TOR via CTRL-006, Ceph expansion&lt;/td&gt;
 &lt;td&gt;Later (needs cable)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="three-things-to-do-before-blades-can-boot-anything"&gt;Three things to do before blades can boot anything
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Identify CPUs.&lt;/strong&gt; Connect to the OA management port, open the web GUI, check CPU model per slot. Ten minutes. Everything else depends on this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Network uplink.&lt;/strong&gt; The blade switches in bays 1 and 2 have 4× RJ45 1GbE uplinks (ports 22–25). Run a patch cable from one to any available switch — MODI, MAGNI, whatever&amp;rsquo;s reachable from the cable box. That&amp;rsquo;s enough for blades to reach DHCP and iPXE.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;RAM redistribution.&lt;/strong&gt; Pull the 14 spare RAM-007 sticks and install into the chosen fat and medium nodes per the profile above.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="the-permanent-vs-experiment-split"&gt;The permanent vs experiment split
&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;Always on (~300–400W total):
 HEIMDAL → OPNsense router, Sun X4150, ~150–200W
 ODEN → Talos, Minecraft + small services, ~100–150W
 LOKE → 2nd Talos node (needs RAM-007 × 8 + SSD boot), ~100–150W

Experiments (fire up, learn, power off):
 ASGARD → 3–16 blades for Slurm / OpenStack / larger Talos cluster
 TYR+TOR+FREJA → Proxmox cluster (M1 DDR2, temporary)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once the Proxmox experiment wraps, TYR, TOR, and FREJA can be powered down permanently. If ASGARD blades eventually become the long-term compute platform, OPNsense can move to a VM on a blade at that point — but not before the blades are stable and trusted. Don&amp;rsquo;t consolidate the router onto experimental infrastructure.&lt;/p&gt;</description></item><item><title>OPNsense in the homelab</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/opnsense/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/opnsense/</guid><description>&lt;p&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/opnsense/" &gt;OPNsense&lt;/a&gt; running on the Sun Fire X4150 — perimeter gateway for both the homelab and the home network.&lt;/p&gt;
&lt;p&gt;Current state: inherited setup, not clean, not properly documented. It works, but it was not built with intention. A redo is on the todo list.&lt;/p&gt;
&lt;p&gt;Dual role is intentional and will stay that way — OPNsense on the Sun Fire handles both the homelab and the house. One box, one gateway.&lt;/p&gt;
&lt;p&gt;The Sun Fire X4150 handles it without breaking a sweat. Old, but solid.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="todo-clean-reinstall"&gt;TODO: Clean reinstall
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Fresh OPNsense install on the Sun Fire&lt;/li&gt;
&lt;li&gt;Rename from &lt;code&gt;router.mjnet.info&lt;/code&gt; → &lt;code&gt;heimdal.mjnet.info&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Document the full configuration: firewall rules, DHCP, DNS (Unbound), TFTP/PXE, BGP peering with VyOS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;On the hostname rename&lt;/strong&gt;: making the router&amp;rsquo;s hostname publicly resolvable is a mild information disclosure — it signals which host is the perimeter device. In practice the IP is what matters, and if it&amp;rsquo;s already reachable the name adds little. Still worth being deliberate about what resolves publicly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="intended-role"&gt;Intended role
&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;ISP/WAN → OPNsense (heimdal.mjnet.info) → LAN switch → rack
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Perimeter firewall and NAT gateway&lt;/li&gt;
&lt;li&gt;DHCP for the LAN&lt;/li&gt;
&lt;li&gt;DNS via Unbound&lt;/li&gt;
&lt;li&gt;TFTP/PXE for bare-metal provisioning&lt;/li&gt;
&lt;li&gt;eBGP peer upstream of VyOS (future — see &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/vyos-bgp/" &gt;VyOS + BGP&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Proxmox Cluster in the homelab</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/proxmox-cluster/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/proxmox-cluster/</guid><description>&lt;p&gt;Getting a three-node &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cloud-infrastructure/proxmox/" &gt;Proxmox VE&lt;/a&gt; cluster running in the homelab.&lt;/p&gt;
&lt;p&gt;The goal is a shared virtualization platform for running VMs and LXC containers across the rack. Also, a good excuse to kick the tires on Proxmox itself so, naturally, let&amp;rsquo;s needlessly complicate things with some self-imposed constraints:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;run it clustered&lt;/li&gt;
&lt;li&gt;don&amp;rsquo;t use any hardware already earmarked for other projects&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="hardware"&gt;Hardware
&lt;/h2&gt;&lt;p&gt;I am going try and use three IBM rack servers from the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/inventory/systems/" &gt;inventory&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Asset ID&lt;/th&gt;
 &lt;th&gt;Hostname&lt;/th&gt;
 &lt;th&gt;Model&lt;/th&gt;
 &lt;th&gt;Form Factor&lt;/th&gt;
 &lt;th&gt;RAM&lt;/th&gt;
 &lt;th&gt;CPU&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;SYS-001&lt;/td&gt;
 &lt;td&gt;FREJA&lt;/td&gt;
 &lt;td&gt;IBM System x3550 M1 (7978)&lt;/td&gt;
 &lt;td&gt;1U&lt;/td&gt;
 &lt;td&gt;24GB&lt;/td&gt;
 &lt;td&gt;single&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SYS-002&lt;/td&gt;
 &lt;td&gt;TYR&lt;/td&gt;
 &lt;td&gt;IBM System x3650 M1 (7979)&lt;/td&gt;
 &lt;td&gt;2U&lt;/td&gt;
 &lt;td&gt;64GB&lt;/td&gt;
 &lt;td&gt;dual&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SYS-003&lt;/td&gt;
 &lt;td&gt;TOR&lt;/td&gt;
 &lt;td&gt;IBM System x3650 M1 (7979)&lt;/td&gt;
 &lt;td&gt;2U&lt;/td&gt;
 &lt;td&gt;64GB&lt;/td&gt;
 &lt;td&gt;dual&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Three nodes satisfies Corosync quorum without needing a &lt;code&gt;qdevice&lt;/code&gt; — losing one node still leaves a majority.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="installation"&gt;Installation
&lt;/h2&gt;&lt;p&gt;&lt;em&gt;In progress.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="cluster-formation"&gt;Cluster formation
&lt;/h2&gt;&lt;p&gt;&lt;em&gt;In progress.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Rook + Ceph on ODEN</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/rook-ceph/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/rook-ceph/</guid><description>&lt;p&gt;Attempting to add persistent block storage to the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/inventory/systems/" &gt;ODEN&lt;/a&gt; single-node Talos cluster using &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/rook/" &gt;Rook&lt;/a&gt; and &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cloud-infrastructure/ceph/" &gt;Ceph&lt;/a&gt;. This did not fully succeed — the setup reached the point of a bound PVC and a working write test, but the cluster was not left in a clean stable state. Notes are here for completeness.&lt;/p&gt;
&lt;p&gt;This builds on the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/talos-omni/" &gt;Talos cluster setup on ODEN&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hardware"&gt;Hardware
&lt;/h2&gt;&lt;p&gt;ODEN has five storage devices:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Device&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Size&lt;/th&gt;
 &lt;th&gt;Role&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/dev/sdb&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Kingston SA400S3 SSD (SATA)&lt;/td&gt;
 &lt;td&gt;120 GB&lt;/td&gt;
 &lt;td&gt;Boot disk — leave alone&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/dev/nvme0n1&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Samsung 970 EVO NVMe&lt;/td&gt;
 &lt;td&gt;500 GB&lt;/td&gt;
 &lt;td&gt;OSD&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/dev/sdc&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Kingston SA400S3 SSD (SATA)&lt;/td&gt;
 &lt;td&gt;120 GB&lt;/td&gt;
 &lt;td&gt;OSD&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/dev/sdd&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Kingston SA400S3 SSD (SATA)&lt;/td&gt;
 &lt;td&gt;120 GB&lt;/td&gt;
 &lt;td&gt;OSD&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/dev/sde&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Kingston SA400S3 SSD (SATA)&lt;/td&gt;
 &lt;td&gt;120 GB&lt;/td&gt;
 &lt;td&gt;OSD&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Do not add &lt;code&gt;/dev/sdb&lt;/code&gt; to Ceph. It is the boot disk.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-1--install-the-rook-operator"&gt;Step 1 — Install the Rook operator
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/rook/rook/refs/tags/v1.17.9/deploy/examples/crds.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/rook/rook/refs/tags/v1.17.9/deploy/examples/common.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/rook/rook/refs/tags/v1.17.9/deploy/examples/operator.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wait for the operator pod to be running in &lt;code&gt;rook-ceph&lt;/code&gt; namespace before continuing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-2--cephcluster-single-node"&gt;Step 2 — CephCluster (single-node)
&lt;/h2&gt;&lt;p&gt;Single-node requires &lt;code&gt;allowMultiplePerNode: true&lt;/code&gt; and explicit disk selection. The cluster-test example from the Rook repo is a reasonable starting point:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;storage&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;useAllNodes&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;nodes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;192.168.1.171&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;devices&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;nvme0n1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;sdc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;sdd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;sde&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reference: &lt;a class="link" href="https://github.com/rook/rook/blob/release-1.17/deploy/examples/cluster-test.yaml" target="_blank" rel="noopener"
 &gt;cluster-test.yaml&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-3--cephblockpool-and-storageclass"&gt;Step 3 — CephBlockPool and StorageClass
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ceph.rook.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;CephBlockPool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;replicapool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;rook-ceph&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;replicated&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;size&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;storage.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;StorageClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;rook-ceph-block&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;provisioner&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;rook-ceph.rbd.csi.ceph.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;parameters&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;clusterID&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;rook-ceph&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pool&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;replicapool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;imageFormat&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;imageFeatures&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;layering&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;reclaimPolicy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="step-4--pvc-test"&gt;Step 4 — PVC test
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;test-pvc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;accessModes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;ReadWriteOnce&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;storageClassName&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;rook-ceph-block&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;storage&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10Gi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;PVC reached &lt;code&gt;Bound&lt;/code&gt;. A BusyBox pod mounting it could write to &lt;code&gt;/mnt&lt;/code&gt;. The Ceph dashboard (&lt;code&gt;kubectl -n rook-ceph port-forward svc/rook-ceph-mgr-dashboard 7000:7000&lt;/code&gt;) showed OSDs active and the pool present.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-did-not-work"&gt;What did not work
&lt;/h2&gt;&lt;p&gt;The cluster ran but was not left stable. Single-node Ceph produces health warnings by design (no redundancy, no failure domain separation). More importantly, the setup was not revisited after initial testing and there are unresolved questions about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSI driver behaviour on Talos (Talos has specific requirements for CSI socket paths)&lt;/li&gt;
&lt;li&gt;Whether the dashboard warnings were cosmetic or indicated real issues&lt;/li&gt;
&lt;li&gt;Long-term stability under actual workloads&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is left as a draft until there is time to run it properly — ideally on more than one node.&lt;/p&gt;</description></item><item><title>Talos Linux in the homelab via Omni</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/talos-omni/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/talos-omni/</guid><description>&lt;p&gt;Getting &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/talos/" &gt;Talos Linux&lt;/a&gt; running in the homelab via PXE boot and &lt;a class="link" href="https://omni.siderolabs.com" target="_blank" rel="noopener"
 &gt;Omni&lt;/a&gt; — starting with &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/inventory/systems/" &gt;ODEN (SYS-005)&lt;/a&gt;, an IBM System x3550 M3. The full OPNSense + iPXE configuration lives in the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/hardware/hardware-provisioning/ipxe-opnsense/" &gt;reference note&lt;/a&gt;; this covers what actually happened, in order.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="setup"&gt;Setup
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;: ODEN (SYS-005) — IBM x3550 M3, Broadcom BNX2 NICs (BCM5709)&lt;br&gt;
&lt;strong&gt;Network&lt;/strong&gt;: OPNSense router on LAN; ODEN connected via one NIC (start with one — removes variables)&lt;br&gt;
&lt;strong&gt;Target&lt;/strong&gt;: Single-node Talos cluster registered in Omni&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-1--opnsense-dhcp-and-tftp"&gt;Step 1 — OPNSense DHCP and TFTP
&lt;/h2&gt;&lt;p&gt;Enable network booting on the LAN DHCP server and download the iPXE binaries to the TFTP root. Full field values in the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/hardware/hardware-provisioning/ipxe-opnsense/" &gt;iPXE reference note&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One thing to check first: if you previously set DHCP options 66 and 67 as raw additional options, remove them. OPNSense&amp;rsquo;s built-in network boot fields do the same job and having both causes conflicts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-2--ipxe-boot-script"&gt;Step 2 — iPXE boot script
&lt;/h2&gt;&lt;p&gt;Write &lt;code&gt;default.ipxe&lt;/code&gt; to &lt;code&gt;/usr/local/tftp/&lt;/code&gt;. Include a boot menu with at minimum a Talos option and a shell fallback — the shell is genuinely useful when something fails and you need to debug from the boot prompt. Full script in the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/hardware/hardware-provisioning/ipxe-opnsense/" &gt;reference note&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Talos entry in the menu needs the Omni join token from your Omni console. Generate a join link in Omni; it provides the API endpoint, token, and SideroLink addresses.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-3--talos-kernel-and-initramfs"&gt;Step 3 — Talos kernel and initramfs
&lt;/h2&gt;&lt;p&gt;The standard Talos release binaries do not include BNX2 firmware. Since around Talos 1.6 those drivers are available as extensions but not in the mainline image. Without them, the node boots, fails to initialise the NIC, and produces &lt;code&gt;can't load firmware bnx2&lt;/code&gt; errors — everything else looks fine until you notice the node never gets an IP and never appears in Omni.&lt;/p&gt;
&lt;p&gt;Fix: generate a custom image at &lt;a class="link" href="https://factory.talos.dev" target="_blank" rel="noopener"
 &gt;factory.talos.dev&lt;/a&gt; with the &lt;code&gt;siderolabs/bnx2&lt;/code&gt; extension included, then download the PXE kernel and initramfs from the factory URL. Commands in the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/hardware/hardware-provisioning/ipxe-opnsense/" &gt;reference note&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-4--first-boot"&gt;Step 4 — First boot
&lt;/h2&gt;&lt;p&gt;Go into BIOS and set the boot device to PXE. On the M3, UEFI boot with &lt;code&gt;ipxe.efi&lt;/code&gt; fails silently — the image is too large for the NIC&amp;rsquo;s PXE memory buffer. Switch to legacy/BIOS mode and use &lt;code&gt;undionly.kpxe&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;The machine takes a while to POST and boot. This is normal for old enterprise hardware. It is also why demos typically use virtual machines.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-5--static-ip"&gt;Step 5 — Static IP
&lt;/h2&gt;&lt;p&gt;After the BNX2 fix the node boots Talos successfully but still does not appear in Omni. The DHCP assignment for the node is not being picked up during early boot. Workaround: add a static IP via kernel params in the iPXE script:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-ipxe" data-lang="ipxe"&gt;ip=192.168.1.171::192.168.1.1:255.255.255.0::eth0:off
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add this to the &lt;code&gt;kernel&lt;/code&gt; line in the Talos iPXE entry. The format is &lt;code&gt;ip=&amp;lt;client-ip&amp;gt;::&amp;lt;gateway&amp;gt;:&amp;lt;netmask&amp;gt;::&amp;lt;iface&amp;gt;:off&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-6--omni-registration"&gt;Step 6 — Omni registration
&lt;/h2&gt;&lt;p&gt;With a working NIC and an IP, the node contacts the Omni API using the join token. It appears in the Omni console as an unallocated machine. Create a cluster, assign the machine, and let Omni configure it. The initial cluster bootstrap takes a few minutes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-7--fix-the-bios-boot-order"&gt;Step 7 — Fix the BIOS boot order
&lt;/h2&gt;&lt;p&gt;After the cluster is up, change the BIOS boot order so the disk is first. If PXE remains the primary boot device, every reboot drops the machine back to the iPXE menu instead of booting the installed Talos. Discovered on first reboot. Worth noting it here so you don&amp;rsquo;t make the same trip to the garage.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="upgrade"&gt;Upgrade
&lt;/h2&gt;&lt;p&gt;Omni makes single-node upgrades straightforward: open the cluster in the Omni console, select a new Talos version, apply. The node reboots once. Single-node means the cluster has downtime during the reboot; that is expected. Nothing else to do.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="result"&gt;Result
&lt;/h2&gt;&lt;p&gt;Single-node Kubernetes cluster running on ODEN, managed via Omni. &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;talosctl&lt;/code&gt; access via the Omni CLI. Next experiment: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rook-ceph/" &gt;Rook + Ceph&lt;/a&gt; for persistent storage.&lt;/p&gt;</description></item><item><title>3D Printed Rack Parts</title><link>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-3d-prints/</link><pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/homelab/rack-3d-prints/</guid><description>&lt;p&gt;Filling gaps in the rack build with printed parts — ears, blanks, and a modular tray system. Mix of sourced models from Printables and geometry scripted in Blender Python.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="sourced-models"&gt;Sourced Models
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Model&lt;/th&gt;
 &lt;th&gt;What it is&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1335735-1u-universal-rack-ears" target="_blank" rel="noopener"
 &gt;1U Universal Rack Ears&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Rack ears for gear that ships without them&lt;/td&gt;
 &lt;td&gt;Printed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1374690-standard-rack-1u-2u-3u-4u-spacer-dxf-template" target="_blank" rel="noopener"
 &gt;1U–4U Spacer / Blank Panel&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Blank panels to fill empty rack units&lt;/td&gt;
 &lt;td&gt;Printed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://makerworld.com/en/models/1040867-rackmod-1u-slide-a-modular-server-rack-system" target="_blank" rel="noopener"
 &gt;RackMod 1U Slide-A&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Modular 1U tray system with slide-in modules&lt;/td&gt;
 &lt;td&gt;Testing&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1184008-19-1u-server-rack-cover-plates-update-2u-3u" target="_blank" rel="noopener"
 &gt;19&amp;quot; 1U/2U/3U Cover Plates&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Solid cover/blanking plates for 19&amp;quot; rack&lt;/td&gt;
 &lt;td&gt;Queued&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1350350-1u-rack-cable-ears" target="_blank" rel="noopener"
 &gt;1U Rack Cable Ears&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Rack ears with integrated cable management&lt;/td&gt;
 &lt;td&gt;Queued&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Raspberry Pi 1U rack mount&lt;/td&gt;
 &lt;td&gt;1U mount for RPi in 19&amp;quot; rack (BIFROST)&lt;/td&gt;
 &lt;td&gt;Printed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.printables.com/model/1300803-zip-tie-clip-45-mm-t-slot-extrusion" target="_blank" rel="noopener"
 &gt;Zip Tie Clip 45mm T-Slot&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Cable management clips for 45mm extrusion&lt;/td&gt;
 &lt;td&gt;Queued&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="scripted-parts"&gt;Scripted Parts
&lt;/h2&gt;&lt;p&gt;Geometry generated by a Python script running inside Blender rather than modeled by hand. The immediate need was a support brace for the rack — a 220×150×8mm plate with 40 through-holes in a specific alternating-spacing pattern, the B.E.S.T label engraved into the top face, exported directly to STL.&lt;/p&gt;
&lt;p&gt;Writing it in Python means the layout lives in a config block at the top of the file. Changing hole count, plate dimensions, or row spacing is a constant, not a modeling operation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/" &gt;Rack Support Brace&lt;/a&gt; — 220×150mm plate, 10×4 hole grid, engraved text, automated renders | &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/code/procedural-mesh/rack_support_brace/rack_support_brace.py" &gt;script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More on the approach: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/" &gt;Blender Python for 3D printing&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Print setup&lt;/strong&gt;: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/3d-printing/" &gt;3D Printing — Garage&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>