2026-08-31 19:39:34 -07:00
'use client'
import Link from 'next/link'
import { ArrowLeft } from 'lucide-react'
2026-09-08 19:51:39 -07:00
import { motion } from 'framer-motion'
2026-08-31 19:39:34 -07:00
const skillCategories = [
{
title : 'Infrastructure & Orchestration' ,
skills : [ 'Kubernetes (Talos Linux)' , 'ArgoCD (GitOps)' , 'Terraform (IaC)' , 'Kustomize' ],
},
{
title : 'Networking & Ingress' ,
skills : [ 'nginx Ingress Controller' , 'Cloudflare Tunnel' , 'CoreDNS' , 'NetworkPolicy' ],
},
{
title : 'Storage' ,
skills : [ 'Longhorn (distributed block)' , 'MinIO (S3-compatible)' ],
},
{
title : 'Databases' ,
skills : [ 'CloudNativePG (PostgreSQL)' , 'pgvector (AI embeddings)' ],
},
{
title : 'Identity & Security' ,
skills : [ 'Authentik (OIDC SSO)' , 'SOPS (encrypted secrets)' , 'cert-manager (TLS)' ],
},
{
title : 'Observability' ,
skills : [ 'Prometheus' , 'Grafana' , 'Tempo (tracing)' , 'OpenTelemetry' , 'Loki (logs)' ],
},
{
title : 'CI/CD' ,
skills : [ 'Forgejo (git + Actions)' , 'Container Registry' , 'DinD Runners' ],
},
{
title : 'AI/ML Platform' ,
skills : [ 'vLLM (Qwen3-32B)' , 'Ollama' , 'TEI (embeddings)' , 'KServe' ],
},
{
title : 'Workflow & Messaging' ,
skills : [ 'Temporal (durable workflows)' , 'Kafka/Redpanda (streaming)' ],
},
{
title : 'Languages & Frameworks' ,
skills : [ 'Go (API gateway)' , 'Python (ML)' , 'Next.js (frontend)' ],
},
]
export default function HomelabPage() {
return (
< main className = "min-h-screen bg-white dark:bg-gray-950" >
2026-09-08 19:51:39 -07:00
< div className = "max-w-6xl mx-auto px-6 py-20" >
2026-08-31 19:39:34 -07:00
< Link
href = "/#project-homelab"
className = "inline-flex items-center gap-2 text-blue-600 dark:text-blue-400 hover:underline mb-8"
>
< ArrowLeft size = { 16 } />
Back to Projects
</ Link >
2026-09-08 19:51:39 -07:00
{ /* Hero */ }
< motion.div
initial = {{ opacity : 0 , y : 20 }}
animate = {{ opacity : 1 , y : 0 }}
transition = {{ duration : 0.6 }}
className = "mb-16"
>
< h1 className = "text-5xl font-bold text-gray-900 dark:text-white mb-4" >
Building AWS at Home
</ h1 >
< p className = "text-xl text-gray-600 dark:text-gray-400 mb-6" >
A 3 - year journey through distributed systems , from Step Functions to LLM inference .
</ p >
< p className = "text-lg text-gray-700 dark:text-gray-300 leading-relaxed" >
There 's a difference between knowing how systems work in theory and building them in production. I' ve spent the last 3 years learning this difference the hard way — first at AWS , then at RBC , and now at home . This is the story of how I learned distributed systems by owning every layer : from workflow orchestration to hardware networking , from GitOps to AI agents .
</ p >
</ motion.div >
2026-08-31 19:39:34 -07:00
2026-09-08 19:51:39 -07:00
{ /* Chapter 1: Homelab */ }
< motion.section
initial = {{ opacity : 0 , y : 40 }}
whileInView = {{ opacity : 1 , y : 0 }}
viewport = {{ once : true }}
className = "mb-16 border-l-4 border-purple-500 pl-8"
>
< div className = "mb-6" >
< div className = "text-sm font-semibold text-purple-600 dark:text-purple-400 uppercase tracking-wide mb-2" >
The Journey
2026-08-31 19:39:34 -07:00
</ div >
2026-09-08 19:51:39 -07:00
< h2 className = "text-3xl font-bold text-gray-900 dark:text-white mb-2" >
Homelab : Building AWS from Scratch
</ h2 >
< p className = "text-gray-600 dark:text-gray-400 font-medium" > May 2025 – Present </ p >
</ div >
< div className = "prose prose-lg dark:prose-invert max-w-none mb-6" >
< p className = "text-gray-700 dark:text-gray-300" >
The question : < strong > How does LLM serving work at scale ? </ strong > The only way to answer that was to build an entire cloud - like platform with SaaS fundamentals from scratch . 4 bare - metal machines , 1 GPU node , 1 Dell PowerEdge ( now in the garage due to noise ), and 2 mini - desktops . All running Talos Linux , a Kubernetes - native OS designed for immutability .
</ p >
</ div >
{ /* Infrastructure section + diagram */ }
< div className = "mb-8" >
< h3 className = "text-2xl font-bold text-gray-900 dark:text-white mb-4" >
Infrastructure Layer
</ h3 >
< div className = "bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800 mb-6" >
< p className = "text-gray-700 dark:text-gray-300 mb-4" >
4 - node bare - metal cluster ( 3 control plane + 1 worker ) running Talos Linux — immutable , API - driven OS designed for Kubernetes . All wired ethernet to avoid etcd consensus issues .
</ p >
< ul className = "space-y-2 text-sm text-gray-600 dark:text-gray-400" >
< li > • < strong > Compute : </ strong > Talos Linux nodes , machine config via Terraform </ li >
< li > • < strong > Networking : </ strong > Cilium CNI , nginx ingress , Cloudflare Tunnel for zero - trust external access </ li >
< li > • < strong > Storage : </ strong > Longhorn for distributed block storage with disk tagging , MinIO for S3 </ li >
< li > • < strong > GitOps : </ strong > ArgoCD with multi - source Applications and sync waves </ li >
</ ul >
2026-08-31 19:39:34 -07:00
</ div >
2026-09-08 19:51:39 -07:00
{ /* Cluster Topology Diagram */ }
< div className = "mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow" >
< div className = "p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800" >
< h4 className = "font-semibold text-gray-900 dark:text-white" > Talos Cluster Topology </ h4 >
< p className = "text-xs text-gray-600 dark:text-gray-400 mt-1" > Control planes , worker GPU , storage , and networking architecture </ p >
</ div >
< div className = "w-full" style = {{ height : '700px' }}>
< iframe
src = "/diagrams/homelab-cluster.html"
className = "w-full h-full border-none"
title = "Talos Cluster Topology"
/>
</ div >
2026-08-31 19:39:34 -07:00
</ div >
</ div >
2026-09-08 19:51:39 -07:00
{ /* GitOps section + diagram */ }
< div className = "mb-8" >
< h3 className = "text-2xl font-bold text-gray-900 dark:text-white mb-4" >
GitOps Evolution
</ h3 >
< div className = "bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800 mb-6" >
< p className = "text-gray-700 dark:text-gray-300 mb-4" >
Started with Terraform for everything — but constant reconciliation of pods and configmaps created chaos . Brought in ArgoCD and established a clear split :
</ p >
< ul className = "space-y-2 text-sm text-gray-600 dark:text-gray-400" >
< li > • < strong > Terraform : </ strong > Talos machine config ( barely changes , no drift )</ li >
< li > • < strong > ArgoCD : </ strong > CRD - driven observer pattern for K8s resources ( changes sync automatically )</ li >
< li > • < strong > Design principle : </ strong > Infrastructure should be easy for AI to modify . Declarative config + clear abstractions = easier for models to reason about </ li >
</ ul >
</ div >
{ /* GitOps Workflow Diagram */ }
< div className = "mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow" >
< div className = "p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800" >
< h4 className = "font-semibold text-gray-900 dark:text-white" > GitOps Deployment Flow </ h4 >
< p className = "text-xs text-gray-600 dark:text-gray-400 mt-1" > ArgoCD for Kubernetes and Terraform for Talos infrastructure </ p >
</ div >
< div className = "w-full" style = {{ height : '700px' }}>
< iframe
src = "/diagrams/homelab-gitops.html"
className = "w-full h-full border-none"
title = "GitOps Workflow"
/>
</ div >
</ div >
</ div >
{ /* AI/ML Platform */ }
< div className = "mb-8" >
< h3 className = "text-2xl font-bold text-gray-900 dark:text-white mb-4" >
AI / ML Platform
</ h3 >
< div className = "bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800 mb-6" >
< p className = "text-gray-700 dark:text-gray-300 mb-4" >
Self - hosted LLM inference with multi - tenant GPU scheduling and 60 % latency reduction .
</ p >
< ul className = "space-y-2 text-sm text-gray-600 dark:text-gray-400" >
< li > • < strong > Inference : </ strong > vLLM serving Qwen3 - 32 B with INT4 quantization </ li >
< li > • < strong > Multi - model : </ strong > Ollama for smaller models , hot - swapping </ li >
< li > • < strong > Embeddings : </ strong > TEI for text embeddings and reranking </ li >
< li > • < strong > Orchestration : </ strong > KServe + custom Go API gateway </ li >
< li > • < strong > Scheduling : </ strong > GPU node affinity with sm70 / Volta constraints </ li >
</ ul >
</ div >
{ /* LLM Stack Diagram */ }
< div className = "mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow" >
< div className = "p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800" >
< h4 className = "font-semibold text-gray-900 dark:text-white" > LLM Inference Architecture </ h4 >
< p className = "text-xs text-gray-600 dark:text-gray-400 mt-1" > Gateway routing to vLLM , Ollama , and TEI on GPU worker node </ p >
</ div >
< div className = "w-full" style = {{ height : '700px' }}>
< iframe
src = "/diagrams/homelab-llm-stack.html"
className = "w-full h-full border-none"
title = "LLM Inference Stack"
/>
</ div >
</ div >
{ /* Request Lifecycle Diagram */ }
< div className = "mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow" >
< div className = "p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800" >
< h4 className = "font-semibold text-gray-900 dark:text-white" > Request Lifecycle </ h4 >
< p className = "text-xs text-gray-600 dark:text-gray-400 mt-1" > Client → nginx → gateway → predictor → streaming response </ p >
</ div >
< div className = "w-full" style = {{ height : '700px' }}>
< iframe
src = "/diagrams/homelab-api-gateway-sequence.html"
className = "w-full h-full border-none"
title = "API Request Flow"
/>
</ div >
</ div >
</ div >
{ /* Data & Security */ }
< div className = "grid grid-cols-1 md:grid-cols-2 gap-6 mb-8" >
< div className = "bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800" >
< h3 className = "font-semibold text-gray-900 dark:text-white mb-3" >
Data Platform
</ h3 >
< ul className = "space-y-2 text-sm text-gray-600 dark:text-gray-400" >
< li > • < strong > PostgreSQL : </ strong > CloudNativePG operator with HA , automated failover </ li >
< li > • < strong > Vector DB : </ strong > pgvector extension for AI embeddings </ li >
< li > • < strong > Streaming : </ strong > Kafka / Redpanda for event - driven architecture </ li >
< li > • < strong > Workflows : </ strong > Temporal for durable , long - running processes </ li >
</ ul >
</ div >
< div className = "bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800" >
< h3 className = "font-semibold text-gray-900 dark:text-white mb-3" >
Identity & Security
</ h3 >
< ul className = "space-y-2 text-sm text-gray-600 dark:text-gray-400" >
< li > • < strong > SSO : </ strong > Authentik OIDC provider with custom claims </ li >
< li > • < strong > RBAC : </ strong > Kubernetes RBAC synced with Authentik groups </ li >
< li > • < strong > Secrets : </ strong > SOPS - encrypted secrets in git , decrypted at deploy time </ li >
< li > • < strong > TLS : </ strong > cert - manager with DNS - 01 ACME via Cloudflare </ li >
</ ul >
</ div >
</ div >
< div className = "bg-purple-50 dark:bg-purple-900/30 border-l-4 border-purple-500 p-4 rounded-r" >
< div className = "font-semibold text-purple-900 dark:text-purple-200 mb-1" > Key Learnings </ div >
< ul className = "text-purple-800 dark:text-purple-100 text-sm space-y-1" >
< li > • Powerline adapters killed etcd consensus at 200 ms latency . Ethernet to garage solved it .</ li >
< li > • Network is foundational — hardware matters more than code sometimes .</ li >
< li > • Design infrastructure so AI can modify it easily . Declarative + immutable = no surprises .</ li >
</ ul >
</ div >
</ motion.section >
{ /* Chapter 4 */ }
< motion.section
initial = {{ opacity : 0 , y : 40 }}
whileInView = {{ opacity : 1 , y : 0 }}
viewport = {{ once : true }}
className = "mb-16 border-l-4 border-yellow-500 pl-8"
>
< div className = "mb-6" >
< div className = "text-sm font-semibold text-yellow-600 dark:text-yellow-400 uppercase tracking-wide mb-2" >
Next : The AI System
</ div >
< h2 className = "text-3xl font-bold text-gray-900 dark:text-white mb-2" >
Poimen : The AI Agent System
</ h2 >
< p className = "text-gray-600 dark:text-gray-400 font-medium" > Building </ p >
</ div >
< div className = "prose prose-lg dark:prose-invert max-w-none mb-6" >
< p className = "text-gray-700 dark:text-gray-300" >
Current LLM architecture works seamlessly with lambda / serverless patterns . The key insight : < strong > if AI is powerful because of context , tool - calling , and memory , what if we provide the right context in the right environment ? </ strong >
</ p >
</ div >
< ul className = "space-y-3 text-gray-700 dark:text-gray-300 mb-6" >
< li className = "flex gap-3" >
< span className = "text-yellow-500 font-bold flex-shrink-0" > • </ span >
< span >< strong > poimen - memory : </ strong > Graph - RAG with wiki - link indexing , 7 B model for instruct QA , pgvector + OpenSearch hybrid search </ span >
</ li >
< li className = "flex gap-3" >
< span className = "text-yellow-500 font-bold flex-shrink-0" > • </ span >
< span >< strong > poimen - workflows : </ strong > Natural language → executable WorkflowSpec via reasoning model , Temporal for durable execution </ span >
</ li >
< li className = "flex gap-3" >
< span className = "text-yellow-500 font-bold flex-shrink-0" > • </ span >
< span >< strong > Activity Knowledge Base : </ strong > Informs LLM about timeouts , retry policies , dependencies before routing </ span >
</ li >
< li className = "flex gap-3" >
< span className = "text-yellow-500 font-bold flex-shrink-0" > • </ span >
< span >< strong > Generic state machine : </ strong > JSON workflow spec + JSONPath parameter chaining . Temporal enables redrive + free self - hosted </ span >
</ li >
</ ul >
< div className = "bg-yellow-50 dark:bg-yellow-900/30 border-l-4 border-yellow-500 p-4 rounded-r" >
< div className = "font-semibold text-yellow-900 dark:text-yellow-200 mb-1" > The Vision </ div >
< p className = "text-yellow-800 dark:text-yellow-100 text-sm" >
Build a skill factory where a model generates workflows and completes them . Each Temporal activity is independently refined — enabling a general - purpose workflow orchestrator that improves through iteration .
</ p >
</ div >
</ motion.section >
2026-08-31 19:39:34 -07:00
{ /* Skills Grid */ }
2026-09-08 19:51:39 -07:00
< motion.section
initial = {{ opacity : 0 }}
whileInView = {{ opacity : 1 }}
viewport = {{ once : true }}
className = "mb-12 mt-24 pt-16 border-t border-gray-200 dark:border-gray-800"
>
< h2 className = "text-4xl font-bold text-gray-900 dark:text-white mb-12" >
Technologies & Skills Mastered
2026-08-31 19:39:34 -07:00
</ h2 >
2026-09-08 19:51:39 -07:00
< div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8" >
{ skillCategories . map (( category , idx ) => (
< motion.div
2026-08-31 19:39:34 -07:00
key = { category . title }
2026-09-08 19:51:39 -07:00
initial = {{ opacity : 0 , y : 20 }}
whileInView = {{ opacity : 1 , y : 0 }}
viewport = {{ once : true }}
transition = {{ delay : idx * 0.05 }}
className = "bg-gradient-to-br from-blue-50 to-purple-50 dark:from-gray-900 dark:to-gray-800 rounded-lg p-8 border border-gray-200 dark:border-gray-800 hover:shadow-lg transition-shadow h-full"
2026-08-31 19:39:34 -07:00
>
2026-09-08 19:51:39 -07:00
< h3 className = "font-bold text-gray-900 dark:text-white mb-6 text-xl" >
2026-08-31 19:39:34 -07:00
{ category . title }
</ h3 >
2026-09-08 19:51:39 -07:00
< div className = "flex flex-wrap gap-3" >
2026-08-31 19:39:34 -07:00
{ category . skills . map (( skill ) => (
< span
key = { skill }
2026-09-08 19:51:39 -07:00
className = "text-base px-4 py-2 rounded-full bg-white dark:bg-gray-700 text-blue-700 dark:text-blue-300 font-semibold border border-blue-200 dark:border-blue-600 hover:border-blue-400 dark:hover:border-blue-400 transition-colors"
2026-08-31 19:39:34 -07:00
>
{ skill }
</ span >
))}
</ div >
2026-09-08 19:51:39 -07:00
</ motion.div >
2026-08-31 19:39:34 -07:00
))}
</ div >
2026-09-08 19:51:39 -07:00
</ motion.section >
2026-08-31 19:39:34 -07:00
</ div >
</ main >
)
}