Source Code
src/pages/projects/home_lab.rs
use leptos::*;
use leptos_meta::{Meta, Title};
use super::data::ALL_PROJECTS;
#[component]
pub fn HomeLab() -> impl IntoView {
let project = ALL_PROJECTS.iter().find(|p| p.slug == "home-lab").unwrap();
let skills_view = project
.skills
.iter()
.map(|&s| view! { <li>{s}</li> })
.collect::<Vec<_>>();
view! {
<Title text="Home Lab – Peter Pinto"/>
<Meta name="description" content="A three-node bare-metal Kubernetes cluster running RKE2, Istio, and Longhorn on repurposed hardware."/>
<div class="page">
<span class="eyebrow">"Projects"</span>
<h1>"Home " <em style="font-style:italic; color: var(--accent)">"Lab"</em></h1>
<p class="lead">
"Three old computers running a hardened, highly-available Kubernetes cluster —
hosting message queues, databases, a git runner, and whatever else needs a home."
</p>
<ul class="skills-list" style="margin-top: 1.5rem;">
{skills_view}
</ul>
<hr class="divider"/>
// ── Cluster ───────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Architecture"</span>
<h2>"Three-Node HA"</h2>
<p>
"The cluster runs "
<a href="https://docs.rke2.io" target="_blank" rel="noopener noreferrer" class="prose-link">"RKE2"</a>
", Rancher's hardened Kubernetes distribution, across three physical machines.
Every node acts as both a control plane member and a worker — there are no
dedicated roles. This means the cluster tolerates losing any single machine
while continuing to schedule and run workloads normally."
</p>
</section>
<hr class="divider"/>
// ── Storage ───────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Storage"</span>
<h2>"Longhorn"</h2>
<p>
"Persistent volumes are backed by "
<a href="https://longhorn.io" target="_blank" rel="noopener noreferrer" class="prose-link">"Longhorn"</a>
", a lightweight distributed block storage engine built for Kubernetes.
Longhorn replicates volume data across nodes so that storage survives individual
machine failures without manual recovery. Each volume can be snapshotted and
restored independently."
</p>
</section>
<hr class="divider"/>
// ── Networking ────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Networking"</span>
<h2>"Istio & Wildcard TLS"</h2>
<p>
"The service mesh is handled by "
<a href="https://istio.io" target="_blank" rel="noopener noreferrer" class="prose-link">"Istio"</a>
", which manages traffic routing and mTLS between services. A wildcard DNS
record points "
<code class="inline-code">"*.k8s.peterpinto.dev"</code>
" at the cluster, so exposing any service externally is a matter of creating
a Gateway resource with the right hostname — TLS is handled automatically."
</p>
</section>
<hr class="divider"/>
// ── Workloads ─────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Workloads"</span>
<h2>"What's Running"</h2>
<p>
"The cluster hosts a mix of infrastructure services and experimental projects,
none of which are intended for public access:"
</p>
<ol class="project-steps">
<li>
<strong>"Gitea Actions Runner"</strong>
" — CI for projects hosted on the local Gitea instance"
</li>
<li>
<strong>"RabbitMQ"</strong>
" — message broker used by internal services"
</li>
<li>
<strong>"Matrix"</strong>
" — self-hosted messaging server"
</li>
<li>
<strong>"InfluxDB"</strong>
" — time-series database for metrics and sensor data"
</li>
<li>
<strong>"SpacetimeDB"</strong>
" — experimental database for a game project in progress"
</li>
</ol>
</section>
<hr class="divider"/>
// ── Management ────────────────────────────────────────
<section class="project-section">
<span class="eyebrow">"Management"</span>
<h2>"kubectl & Helm"</h2>
<p>
"Deployments are managed with plain "
<code class="inline-code">"kubectl"</code>
" or Helm depending on complexity. Simple, stateless services get raw manifests
applied directly. Anything with meaningful configuration, versioning, or upgrade
requirements gets a Helm chart — either an upstream chart with a custom "
<code class="inline-code">"values.yaml"</code>
" or a local one written from scratch."
</p>
</section>
</div>
}
}