Update blog with latest homelab additions

This commit is contained in:
juvdiaz 2026-05-26 22:49:22 -06:00
parent 180c1b1cca
commit f5ae4a2746
3 changed files with 145 additions and 33 deletions

View File

@ -12,6 +12,10 @@ $activityKeys = [
'blog_activity_7',
'blog_activity_8',
'blog_activity_9',
'blog_activity_10',
'blog_activity_11',
'blog_activity_12',
'blog_activity_13',
];
$todoKeys = [
@ -27,6 +31,9 @@ $todoKeys = [
'blog_todo_10',
'blog_todo_11',
'blog_todo_12',
'blog_todo_13',
'blog_todo_14',
'blog_todo_15',
];
$stackKeys = [
@ -43,6 +50,9 @@ $stackKeys = [
'blog_stack_11',
'blog_stack_12',
'blog_stack_13',
'blog_stack_14',
'blog_stack_15',
'blog_stack_16',
];
$treeHref = 'homelab-tree.php?lang=' . urlencode($lang);
@ -106,12 +116,25 @@ $stackSourceLinks = [
'blog_stack_12' => [
['label' => 'provisioning/main.tf', 'path' => 'bootstrap/provisioning/main.tf'],
['label' => 'lab.sh Pimox pipeline', 'path' => 'lab.sh'],
['label' => 'provisioning README', 'path' => 'bootstrap/provisioning/README.md'],
],
'blog_stack_13' => [
['label' => 'preseed.cfg', 'path' => 'bootstrap/provisioning/templates/preseed.cfg.tftpl'],
['label' => 'golden-node prepare', 'path' => 'bootstrap/provisioning/templates/golden-node-prepare.sh.tftpl'],
['label' => 'prepare-template', 'path' => 'bootstrap/provisioning/templates/prepare-template.sh.tftpl'],
],
'blog_stack_14' => [
['label' => 'lab.sh worker storage guardrail', 'path' => 'lab.sh'],
['label' => 'README Pimox defaults', 'path' => 'README.md'],
],
'blog_stack_15' => [
['label' => 'lab.sh OpenWrt pipeline', 'path' => 'lab.sh'],
['label' => 'README OpenWrt notes', 'path' => 'README.md'],
],
'blog_stack_16' => [
['label' => 'platform/main.tf', 'path' => 'bootstrap/platform/main.tf'],
['label' => 'README monitoring', 'path' => 'README.md'],
],
];
function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $sourceBase): void {
@ -202,22 +225,22 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
</div>
<div class="diagram-shell" aria-label="Professional homelab architecture diagram">
<svg class="homelab-map" viewBox="0 0 1120 820" role="img" aria-labelledby="homelab-map-title homelab-map-desc">
<svg class="homelab-map" viewBox="0 0 1120 960" role="img" aria-labelledby="homelab-map-title homelab-map-desc">
<title id="homelab-map-title">Homelab architecture map</title>
<desc id="homelab-map-desc">Git push enters Gitea, Gitea Actions validates and builds app images, OpenTofu manages the cluster and provisioning layers, Debian serves PXE and preseed content, Pimox builds Debian VM templates, Argo CD syncs manifests, and the OCI edge routes traffic into Kubernetes services.</desc>
<desc id="homelab-map-desc">Git push enters Gitea, Gitea Actions validates and builds app images, OpenTofu manages the cluster and provisioning layers, Debian serves PXE and preseed content, Pimox builds Debian VM templates and worker clones on NVMe storage, OpenWrt can run as an opt-in firewall VM, Argo CD syncs manifests, and the OCI edge routes traffic into Kubernetes services.</desc>
<defs>
<marker id="map-arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M2,2 L10,6 L2,10 Z" fill="#2b6cb0"></path>
</marker>
</defs>
<rect class="diagram-zone diagram-zone-source" x="24" y="40" width="310" height="740" rx="12"></rect>
<rect class="diagram-zone diagram-zone-source" x="24" y="40" width="310" height="880" rx="12"></rect>
<text class="diagram-zone-title" x="46" y="74">Source, validation, and images</text>
<rect class="diagram-zone diagram-zone-platform" x="382" y="40" width="340" height="740" rx="12"></rect>
<rect class="diagram-zone diagram-zone-platform" x="382" y="40" width="340" height="880" rx="12"></rect>
<text class="diagram-zone-title" x="404" y="74">Debian node 192.168.100.68</text>
<rect class="diagram-zone diagram-zone-runtime" x="770" y="40" width="326" height="740" rx="12"></rect>
<rect class="diagram-zone diagram-zone-runtime" x="770" y="40" width="326" height="880" rx="12"></rect>
<text class="diagram-zone-title" x="792" y="74">Edge access and workloads</text>
<g class="diagram-node node-accent-blue" transform="translate(54 100)">
@ -285,15 +308,22 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
<g class="diagram-node node-accent-green" transform="translate(412 550)">
<rect width="280" height="82" rx="8"></rect>
<text x="18" y="27">Argo CD</text>
<text class="diagram-small" x="18" y="50">container-registry, website</text>
<text class="diagram-small" x="18" y="68">gitea and demos-static apps</text>
<text class="diagram-small" x="18" y="50">registry, gitea, monitoring</text>
<text class="diagram-small" x="18" y="68">website and demos-static apps</text>
</g>
<g class="diagram-node node-accent-orange" transform="translate(412 662)">
<g class="diagram-node node-accent-purple" transform="translate(412 662)">
<rect width="280" height="82" rx="8"></rect>
<text x="18" y="27">Monitoring stack</text>
<text class="diagram-small" x="18" y="50">Prometheus, Grafana, Loki</text>
<text class="diagram-small" x="18" y="68">Mimir, Promtail, exporters</text>
</g>
<g class="diagram-node node-accent-orange" transform="translate(412 774)">
<rect width="280" height="82" rx="8"></rect>
<text x="18" y="27">Storage and backups</text>
<text class="diagram-small" x="18" y="50">OpenEBS retained hostpath PVs</text>
<text class="diagram-small" x="18" y="68">Gitea dumps and external SSD</text>
<text class="diagram-small" x="18" y="50">OpenEBS retained PVs</text>
<text class="diagram-small" x="18" y="68">Gitea dumps and monitoring data</text>
</g>
<g class="diagram-node node-accent-blue" transform="translate(800 92)">
@ -322,13 +352,22 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
<g class="diagram-node node-accent-red" transform="translate(800 466)">
<rect width="258" height="112" rx="8"></rect>
<text x="18" y="28">Orange Pi 5 Plus Pimox</text>
<text class="diagram-small" x="18" y="52">VM 9000 Debian template</text>
<text class="diagram-small" x="18" y="70">OVMF, virtio-scsi, qemu agent</text>
<text class="diagram-small" x="18" y="88">future worker clones</text>
<text class="diagram-small" x="18" y="106">waiting on more disk</text>
<text class="diagram-small" x="18" y="52">VM 9000 template on local</text>
<text class="diagram-small" x="18" y="70">workers on nvme_thin_pool</text>
<text class="diagram-small" x="18" y="88">OVMF, virtio-scsi, qemu agent</text>
<text class="diagram-small" x="18" y="106">idempotent qm automation</text>
</g>
<g class="diagram-node node-accent-teal" transform="translate(800 616)">
<g class="diagram-node node-accent-orange" transform="translate(800 616)">
<rect width="258" height="112" rx="8"></rect>
<text x="18" y="28">OpenWrt firewall VM</text>
<text class="diagram-small" x="18" y="52">VM 9050, opt-in only</text>
<text class="diagram-small" x="18" y="70">vmbr0 WAN, vmbr1 LAN</text>
<text class="diagram-small" x="18" y="88">simple firewall path</text>
<text class="diagram-small" x="18" y="106">DHCP optional, VLANs later</text>
</g>
<g class="diagram-node node-accent-teal" transform="translate(800 766)">
<rect width="258" height="82" rx="8"></rect>
<text x="18" y="27">Local registry :30500</text>
<text class="diagram-small" x="18" y="50">php-website and demos-static</text>
@ -340,17 +379,20 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
<path class="diagram-link" d="M174 400 L174 430"></path>
<path class="diagram-link" d="M174 512 L174 542"></path>
<path class="diagram-link" d="M294 577 C346 577 352 133 412 133"></path>
<path class="diagram-link" d="M294 577 C372 577 722 657 800 657"></path>
<path class="diagram-link" d="M294 577 C372 577 722 807 800 807"></path>
<path class="diagram-link" d="M294 359 C340 359 360 133 412 133"></path>
<path class="diagram-link" d="M294 249 C348 249 360 479 412 479"></path>
<path class="diagram-link" d="M552 174 L552 202"></path>
<path class="diagram-link" d="M552 298 L552 326"></path>
<path class="diagram-link" d="M552 520 L552 550"></path>
<path class="diagram-link" d="M552 632 L552 662"></path>
<path class="diagram-link" d="M552 744 L552 774"></path>
<path class="diagram-link" d="M692 250 C746 250 746 522 800 522"></path>
<path class="diagram-link" d="M692 133 C748 133 748 672 800 672"></path>
<path class="diagram-link" d="M692 591 C748 591 744 378 800 378"></path>
<path class="diagram-link" d="M929 188 L929 218"></path>
<path class="diagram-link" d="M929 300 L929 330"></path>
<path class="diagram-link" d="M929 616 L929 426"></path>
<path class="diagram-link" d="M929 766 L929 426"></path>
<text class="diagram-link-label" x="184" y="198">push</text>
<text class="diagram-link-label" x="196" y="312">workflow</text>
@ -361,10 +403,11 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
<text class="diagram-link-label" x="562" y="195">serve boot</text>
<text class="diagram-link-label" x="566" y="320">join path</text>
<text class="diagram-link-label" x="710" y="494">Pimox template</text>
<text class="diagram-link-label" x="710" y="626">firewall VM</text>
<text class="diagram-link-label" x="710" y="420">sync apps</text>
<text class="diagram-link-label" x="934" y="212">secure tunnel</text>
<text class="diagram-link-label" x="934" y="322">service traffic</text>
<text class="diagram-link-label" x="946" y="604">image pulls</text>
<text class="diagram-link-label" x="946" y="754">image pulls</text>
</svg>
</div>
@ -574,6 +617,54 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
<?php echo $text['blog_a8']; ?>
</p>
</article>
<article class="message question">
<div class="speaker"
data-translate data-key="blog_speaker_question"
data-en="<?php echo htmlspecialchars($en['blog_speaker_question']); ?>">
<?php echo $text['blog_speaker_question']; ?>
</div>
<p data-translate data-key="blog_q9"
data-en="<?php echo htmlspecialchars($en['blog_q9']); ?>">
<?php echo $text['blog_q9']; ?>
</p>
</article>
<article class="message answer">
<div class="speaker"
data-translate data-key="blog_speaker_answer"
data-en="<?php echo htmlspecialchars($en['blog_speaker_answer']); ?>">
<?php echo $text['blog_speaker_answer']; ?>
</div>
<p data-translate data-key="blog_a9"
data-en="<?php echo htmlspecialchars($en['blog_a9']); ?>">
<?php echo $text['blog_a9']; ?>
</p>
</article>
<article class="message question">
<div class="speaker"
data-translate data-key="blog_speaker_question"
data-en="<?php echo htmlspecialchars($en['blog_speaker_question']); ?>">
<?php echo $text['blog_speaker_question']; ?>
</div>
<p data-translate data-key="blog_q10"
data-en="<?php echo htmlspecialchars($en['blog_q10']); ?>">
<?php echo $text['blog_q10']; ?>
</p>
</article>
<article class="message answer">
<div class="speaker"
data-translate data-key="blog_speaker_answer"
data-en="<?php echo htmlspecialchars($en['blog_speaker_answer']); ?>">
<?php echo $text['blog_speaker_answer']; ?>
</div>
<p data-translate data-key="blog_a10"
data-en="<?php echo htmlspecialchars($en['blog_a10']); ?>">
<?php echo $text['blog_a10']; ?>
</p>
</article>
</section>
<section class="activity-log" aria-labelledby="activity-log-title">

View File

@ -55,13 +55,13 @@ return [
'blog_kicker' => 'Homelab field notes',
'blog_title' => 'I accidentally built a tiny CI/CD platform',
'blog_subtitle' => 'A casual conversation about how a Debian box, a Raspberry Pi, an Orange Pi 5 Plus running Pimox, an OCI edge host, and a suspicious amount of stubbornness became a repeatable Kubernetes delivery path.',
'blog_subtitle' => 'A casual conversation about how a Debian box, a Raspberry Pi, an Orange Pi 5 Plus running Pimox, an OCI edge host, a Debian 13 VM template, and a small OpenWrt firewall plan became a repeatable Kubernetes delivery path.',
'blog_speaker_question' => 'Future me, judging',
'blog_speaker_answer' => 'Me, holding coffee',
'blog_q1' => 'Be honest: why build all this instead of just running a couple containers like a normal person?',
'blog_a1' => 'Because apparently I looked at "host a website" and thought, "what if this had a control plane, GitOps, retained storage, an image registry, and several new ways to embarrass myself?" The real goal was practice: provision the infra, keep config in Git, deploy with automation, break it, fix it, and make sure I could rebuild it without relying on shell history and vibes.',
'blog_q2' => 'Why kubeadm? Were managed clusters too emotionally stable?',
'blog_a2' => 'Pretty much. kubeadm keeps the cluster close to the metal, which is a polite way of saying I get to see every sharp edge. The Debian node runs the control plane, the Raspberry Pi joins as an arm64 worker, and Pimox on an Orange Pi 5 Plus now gives me a path to add Debian 13 arm64 VM workers. Suddenly networking, storage, container runtimes, certs, and node recovery are not mysterious cloud magic. They are my problem.',
'blog_a2' => 'Pretty much. kubeadm keeps the cluster close to the metal, which is a polite way of saying I get to see every sharp edge. The Debian node runs the control plane, the Raspberry Pi joins as an arm64 worker, and Pimox on an Orange Pi 5 Plus now gives me a repeatable way to add Debian 13 arm64 VM workers. Suddenly networking, storage, container runtimes, certs, and node recovery are not mysterious cloud magic. They are my problem.',
'blog_q3' => 'So where is the CI/CD part hiding?',
'blog_a3' => 'It is small, but it is real. OpenTofu brings up the cluster, platform, apps, and edge layers. Argo CD watches Git and keeps the cluster honest. Docker Buildx builds the PHP website for linux/arm64, pushes it to the local registry, and then the workload rolls forward. No enterprise dashboard fireworks, just a clean loop that says: Git changed, image built, cluster updated, nobody had to kubectl-edit anything at 2 AM.',
'blog_q4' => 'Why run your own registry and Gitea? Was the simple option unavailable?',
@ -73,10 +73,14 @@ return [
'blog_q7' => 'Can the current cluster actually handle all that, or are we about to smoke the Pi?',
'blog_a7' => 'The Pi survives because the demos are intentionally local-first and now ship as a separate static artifact. The website pod stays a portfolio shell, the demos-static pod serves static bundles, and the user browser does the expensive work. If I later ship real ONNX object detection, Transformers.js, or full video transcoding models, those must lazy-load in the browser or move to a beefier node. The Raspberry Pi is brave, but it is not a GPU wearing a tiny hat.',
'blog_q8' => 'So the lab can now build its own worker nodes?',
'blog_a8' => 'Mostly, yes. Debian now runs a small provisioning layer with dnsmasq, nginx, PXE boot files, GRUB, and a Debian 13 arm64 preseed. OpenTofu talks to Pimox through qm, creates VM 9000, boots it from the network, installs the OS, runs the golden-node prep, disables swap, verifies cgroups, installs containerd and kubeadm tooling, then seals the VM as a template. The only current blocker for cloning more workers is wonderfully physical: add more disk space.',
'blog_a8' => 'Yes, and now with fewer crossed fingers. Debian runs a provisioning layer with dnsmasq, nginx, PXE boot files, GRUB, and a Debian 13 arm64 preseed. OpenTofu talks to Pimox through qm, creates VM 9000 on local storage, boots it from the network, installs the OS, runs golden-node prep, disables swap, verifies cgroups, installs containerd and kubeadm tooling, then seals it as a template. Worker clones are idempotent by VMID and now land on nvme_thin_pool, so local storage stays reserved for the template.',
'blog_q9' => 'And OpenWrt is joining the story too?',
'blog_a9' => 'Only as a simple firewall, not as a networking science project. The pipeline can create an opt-in OpenWrt ARM SystemReady VM, attach vmbr0 as WAN and vmbr1 as LAN, and configure the LAN side without rewriting Orange Pi host networking. DHCP stays optional, and VLANs wait until there is a managed switch and a local test window.',
'blog_q10' => 'What changed on the observability side?',
'blog_a10' => 'Monitoring moved from "someday" to "running." The platform now has Prometheus, Grafana, Loki, Mimir, Promtail, node-exporter, and kube-state-metrics. The next useful step is not installing more dashboards; it is choosing the few alerts that would actually wake me up for the right reasons.',
'blog_stack_title' => 'Technologies and why they are here',
'blog_stack_1' => 'Debian Linux is the steady adult in the room: control plane host, deployment workstation, PXE/preseed server, and the place where OpenTofu, Docker, kubeadm, and the scripts do their thing.',
'blog_stack_2' => 'Raspberry Pi adds the current arm64 worker, while Pimox on the Orange Pi 5 Plus gives the lab a VM-based expansion path once there is enough storage.',
'blog_stack_2' => 'Raspberry Pi adds the current arm64 worker, while Pimox on the Orange Pi 5 Plus gives the lab a VM-based expansion path. The template stays on local storage and Kubernetes worker clones go to nvme_thin_pool.',
'blog_stack_3' => 'OpenTofu makes the cluster, platform, apps, edge, and provisioning layers repeatable, because "I swear I remember the command" is not a disaster recovery strategy.',
'blog_stack_4' => 'Calico handles pod networking, and OpenEBS hostpath storage keeps the important data around after rebuilds, because deleting everything by accident is only funny once.',
'blog_stack_5' => 'Argo CD is the GitOps referee: manifests live in Git, the cluster follows along, and manual drift gets side-eyed back into place.',
@ -86,16 +90,19 @@ return [
'blog_stack_9' => 'The newer demos cover network jitter graphs, local JSON/JWT/log tools, an architecture simulator, an offline traveler converter, a redactor prototype, sentiment analysis, and model-drift simulation.',
'blog_stack_10' => 'The heavier ML demos are designed as client-side Wasm/ONNX/Transformers.js candidates, not server-side jobs. That keeps the homelab app boring to operate, which is secretly the whole point.',
'blog_stack_11' => 'The demo code now builds into its own demos-static image and Argo CD app, exposed at /demo-apps/. The PHP website only owns the catalog link, which is much less cursed.',
'blog_stack_12' => 'The Pimox worker pipeline uses qm over SSH to create an OVMF/virtio-scsi Debian 13 arm64 VM, wait for qemu-guest-agent, seal it, and convert VM 9000 into a reusable template.',
'blog_stack_12' => 'The Pimox worker pipeline uses qm over SSH to create an OVMF/virtio-scsi Debian 13 arm64 VM, wait for qemu-guest-agent, seal it, and convert VM 9000 into a reusable template on local storage.',
'blog_stack_13' => 'The golden image bakes in Kubernetes prerequisites: swap disabled, cgroup boot options checked, kernel modules loaded, containerd configured for systemd cgroups, kubeadm/kubelet/kubectl installed, and qemu-guest-agent enabled.',
'blog_stack_14' => 'Worker clone automation is count-based and idempotent: LAB_PIMOX_WORKER_COUNT=1 means ensure VM 9010 exists, not create a fresh worker on every run. New workers are refused if the target storage is local.',
'blog_stack_15' => 'OpenWrt is handled separately from the Debian golden-node template. The lab downloads the upstream ARM SystemReady EFI image, imports it as VM 9050 on nvme_thin_pool, and keeps it disabled unless LAB_OPENWRT_VM=true is set.',
'blog_stack_16' => 'The monitoring layer now includes Prometheus Stack, Grafana, Mimir, Loki, Promtail, node-exporter, and kube-state-metrics, giving the lab metrics and logs without putting heavy traffic analysis on the firewall.',
'blog_arch_kicker' => 'Architecture map',
'blog_arch_title' => 'The homelab, end to end',
'blog_arch_intro' => 'The current delivery path starts with a push to Gitea, runs local validation, builds arm64 images, syncs the validated commit into the GitOps mirror, and lets Argo CD reconcile the Kubernetes workloads. The infrastructure path stays manual through lab.sh, including the PXE/Pimox template builder, while the OCI edge routes public traffic back through the private path.',
'blog_arch_caption' => 'The diagram is intentionally operational: it shows the app delivery loop, image flow, provisioning path, storage boundary, and public traffic path without hiding the practical bits that make a small lab behave like a platform.',
'blog_arch_intro' => 'The current delivery path starts with a push to Gitea, runs local validation, builds arm64 images, syncs the validated commit into the GitOps mirror, and lets Argo CD reconcile the Kubernetes workloads. The infrastructure path stays manual through lab.sh, including the PXE/Pimox template builder, NVMe-backed worker clones, and the opt-in OpenWrt firewall VM, while the OCI edge routes public traffic back through the private path.',
'blog_arch_caption' => 'The diagram is intentionally operational: it shows the app delivery loop, image flow, provisioning path, storage boundary, monitoring layer, OpenWrt firewall option, and public traffic path without hiding the practical bits that make a small lab behave like a platform.',
'blog_arch_fun_link' => 'Open the Christmas-tree version',
'blog_activity_kicker' => 'Recent activity log',
'blog_activity_title' => 'What changed since the first build',
'blog_activity_intro' => 'The lab moved from a working Kubernetes experiment into a more complete self-hosted delivery system. The latest work focused on trust, repeatability, and making deploys match the exact commit that passed validation.',
'blog_activity_intro' => 'The lab moved from a working Kubernetes experiment into a more complete self-hosted delivery system. The latest work focused on trust, repeatability, VM-based expansion, and making deploys match the exact commit that passed validation.',
'blog_activity_1' => 'Brought Gitea online as the local Git service, including persistent storage and the public /git/ route through the edge stack.',
'blog_activity_2' => 'Installed and validated a Debian-hosted Gitea Actions runner so pushes to main can build, scan, and deploy without depending on a laptop session.',
'blog_activity_3' => 'Added a custom checkout flow for the /git/ subpath and kept a persistent Debian checkout for the deployment scripts.',
@ -105,6 +112,10 @@ return [
'blog_activity_7' => 'Split the demos into a dedicated demos-static image and Argo CD application so the PHP website stays small and boring.',
'blog_activity_8' => 'Fixed Gitea operational details around probes, service paths, backup dumps, and the user context used for safe backup execution.',
'blog_activity_9' => 'Validated the full main-branch deployment path: fetch main, apply OpenTofu layers, build and push arm64 images, refresh Argo CD, and confirm the runner completes successfully.',
'blog_activity_10' => 'Built the Debian 13 arm64 Pimox template end to end with PXE, preseed, qemu-guest-agent discovery, cgroup validation, swap disabled, and a final seal step.',
'blog_activity_11' => 'Added NVMe-backed Pimox worker clone automation so VM 9000 stays on local storage while worker nodes are created on nvme_thin_pool.',
'blog_activity_12' => 'Added an opt-in OpenWrt VM path for a simple firewall between vmbr0 and vmbr1, with guardrails that avoid Orange Pi host networking changes.',
'blog_activity_13' => 'Installed the monitoring stack with Grafana, Prometheus, Mimir, Loki, Promtail, node-exporter, and kube-state-metrics.',
'blog_todo_kicker' => 'Improvement backlog',
'blog_todo_title' => 'Todo list for the next homelab pass',
'blog_todo_intro' => 'These are improvement proposals, not chores for the sake of chores. Each item either reduces rebuild risk, tightens supply-chain hygiene, or makes the platform easier to operate when something fails.',
@ -115,11 +126,14 @@ return [
'blog_todo_5' => 'Generate SBOMs and sign images so the local registry can prove what it is serving.',
'blog_todo_6' => 'Add Renovate or Dependabot-style dependency updates for base images, Helm charts, and GitHub/Gitea Actions.',
'blog_todo_7' => 'Enforce baseline Kubernetes policy with Kyverno or Gatekeeper: non-root, read-only roots, resource requests, and allowed registries.',
'blog_todo_8' => 'Install observability that fits the hardware: Prometheus, Grafana, Loki, node-exporter, and a few high-signal alerts.',
'blog_todo_8' => 'Turn the installed observability stack into useful operations views: a few high-signal dashboards, alerts for node health, storage pressure, certificate expiry, and failed app syncs.',
'blog_todo_9' => 'Schedule backup restore drills for Gitea and OpenEBS volumes, then write the exact restore runbook.',
'blog_todo_10' => 'Tighten TLS, SSH, and token rotation around the OCI edge, Gitea, registry, and runner credentials.',
'blog_todo_11' => 'Design the next storage step before adding more apps: NAS, replicated storage, or a clearly documented single-node tradeoff.',
'blog_todo_11' => 'Document the new storage split: local for the Pimox template, nvme_thin_pool for VM workers, OpenEBS for Kubernetes app data, and backup targets for anything that must survive a rebuild.',
'blog_todo_12' => 'Move sensitive app configuration into Sealed Secrets, External Secrets, or another explicit secret-management path.',
'blog_todo_13' => 'After the new disk has breathing room, clone the first Pimox worker from VM 9000, join it with kubeadm, and verify labels, taints, CNI, storage, and workload scheduling.',
'blog_todo_14' => 'Test the OpenWrt VM in a maintenance window before making it a gateway: confirm WAN, LAN, rollback access, DHCP settings, and that Pimox remains reachable.',
'blog_todo_15' => 'Buy or configure a managed switch before VLAN work. Until then, keep OpenWrt as a simple two-interface firewall and avoid risky remote bridge rewrites.',
'blog_ideas_kicker' => 'Visitor ideas',
'blog_ideas_title' => 'What would you improve next?',
'blog_ideas_intro' => 'Send a practical idea for the homelab backlog. Submissions are stored as plain text, limited in size, and rendered escaped.',

View File

@ -59,13 +59,13 @@ return [
'blog_kicker' => 'Homelab tlahcuilolli',
'blog_title' => 'Tlatecpanaliztli homelab CI/CD pipeline',
'blog_subtitle' => 'Ce tlahtolli in quenin Debian server, Raspberry Pi, Orange Pi 5 Plus ipan Pimox, ihuan OCI edge box mochihua ce Kubernetes tlatequipanoliztli.',
'blog_subtitle' => 'Ce tlahtolli in quenin Debian server, Raspberry Pi, Orange Pi 5 Plus ipan Pimox, OCI edge box, Debian 13 VM template, ihuan OpenWrt firewall plan mochihua ce Kubernetes tlatequipanoliztli.',
'blog_speaker_question' => 'Nehuatl mostla',
'blog_speaker_answer' => 'Nehuatl axcan',
'blog_q1' => 'Tleica niquichihua inin ihuan ahmo zan container tlatequipanoa?',
'blog_a1' => 'Ahmo zan website. Niquinequi nicnemiliz in operating model: infrastructure, Git, automation, recovery, ihuan reproducible rebuild.',
'blog_q2' => 'Tleica kubeadm ihuan ahmo managed Kubernetes?',
'blog_a2' => 'kubeadm quipia cluster nechca metal. Debian quipia control plane, Raspberry Pi mochihua arm64 worker, ihuan Pimox ipan Orange Pi 5 Plus quimaca Debian 13 arm64 VM workers. Ipan inin niquita networking, storage, runtime, certificates, ihuan node recovery.',
'blog_a2' => 'kubeadm quipia cluster nechca metal. Debian quipia control plane, Raspberry Pi mochihua arm64 worker, ihuan Pimox ipan Orange Pi 5 Plus quimaca repeatable Debian 13 arm64 VM workers. Ipan inin niquita networking, storage, runtime, certificates, ihuan node recovery.',
'blog_q3' => 'Canin nemi CI/CD ipan inin setup?',
'blog_a3' => 'Pipeline achi tepiton. OpenTofu quichihua cluster, platform, apps, ihuan edge. Argo CD quitta Git repo ihuan quichihua sync. Docker Buildx quichihua PHP website image para linux/arm64 ihuan quipush ipan local registry.',
'blog_q4' => 'Tleica private registry ihuan Gitea ipan lab?',
@ -77,10 +77,14 @@ return [
'blog_q7' => 'Cluster huel quipias nochi demos?',
'blog_a7' => 'Quena, pampa demos cateh local-first ihuan separate static artifact. Website pod zan shell, demos-static pod quimaca bundles, browser quichihua tequitl. Real ONNX, Transformers.js, o video transcoding monequi lazy-load o occe node hueyi.',
'blog_q8' => 'Axcan lab huel quichihua worker nodes?',
'blog_a8' => 'Quena, achi. Debian quipia provisioning layer: dnsmasq, nginx, PXE, GRUB, ihuan Debian 13 arm64 preseed. OpenTofu notza Pimox ika qm, quichihua VM 9000, quiboota network, quinstala OS, quichihua golden-node prep, quitzacua swap, quitta cgroups, quinstala containerd ihuan kubeadm tools, ihuan quicuepa template. Axcan monequi occe disk para clones.',
'blog_a8' => 'Quena. Debian quipia provisioning layer: dnsmasq, nginx, PXE, GRUB, ihuan Debian 13 arm64 preseed. OpenTofu notza Pimox ika qm, quichihua VM 9000 ipan local storage, quiboota network, quinstala OS, quichihua golden-node prep, quitzacua swap, quitta cgroups, quinstala containerd ihuan kubeadm tools, ihuan quicuepa template. Worker clones yahui ipan nvme_thin_pool.',
'blog_q9' => 'Ihuan OpenWrt no quiza nican?',
'blog_a9' => 'Zan simple firewall. Pipeline huel quichihua OpenWrt ARM SystemReady VM, quipia vmbr0 quen WAN ihuan vmbr1 quen LAN, ihuan amo quipatla Orange Pi host networking. DHCP optional, VLANs mostla quema onca managed switch ihuan local test.',
'blog_q10' => 'Tlein mopatla ipan observability?',
'blog_a10' => 'Monitoring axcan nemi: Prometheus, Grafana, Loki, Mimir, Promtail, node-exporter, ihuan kube-state-metrics. Oc monequi achi cuali dashboards ihuan alerts.',
'blog_stack_title' => 'Tlamantli ihuan tleica nemi nican',
'blog_stack_1' => 'Debian Linux quimaca control-plane host, deployment workstation, PXE/preseed server, ihuan canin nemi OpenTofu, Docker, kubeadm, ihuan scripts.',
'blog_stack_2' => 'Raspberry Pi quimaca axcan arm64 worker; Pimox ipan Orange Pi 5 Plus quimaca VM expansion path quema onca achi disk.',
'blog_stack_2' => 'Raspberry Pi quimaca axcan arm64 worker; Pimox ipan Orange Pi 5 Plus quimaca VM expansion path. Template mocahua ipan local storage ihuan worker clones yahui ipan nvme_thin_pool.',
'blog_stack_3' => 'OpenTofu quichihua cluster, platform, apps, edge, ihuan provisioning configuration reproducible.',
'blog_stack_4' => 'Calico quimati pod networking; OpenEBS hostpath storage quipia data ipan cluster rebuilds.',
'blog_stack_5' => 'Argo CD quimaca GitOps control loop: manifests cateh ipan Git ihuan cluster moyecpana.',
@ -90,8 +94,11 @@ return [
'blog_stack_9' => 'Yancuic demos quipia network jitter graphs, local JSON/JWT/log tools, architecture simulator, offline traveler converter, redactor, sentiment analysis, ihuan model drift simulation.',
'blog_stack_10' => 'ML demos monequi client-side Wasm/ONNX/Transformers.js, ahmo server-side jobs.',
'blog_stack_11' => 'Demo code axcan quichihua demos-static image ihuan Argo CD app, exposed ipan /demo-apps/. PHP website zan catalog.',
'blog_stack_12' => 'Pimox worker pipeline quichihua qm ika SSH para OVMF/virtio-scsi Debian 13 arm64 VM, quichia qemu-guest-agent, quiseala, ihuan quicuepa VM 9000 template.',
'blog_stack_12' => 'Pimox worker pipeline quichihua qm ika SSH para OVMF/virtio-scsi Debian 13 arm64 VM, quichia qemu-guest-agent, quiseala, ihuan quicuepa VM 9000 template ipan local storage.',
'blog_stack_13' => 'Golden image quipia Kubernetes prereqs: swap disabled, cgroup boot options checked, kernel modules, containerd systemd cgroups, kubeadm/kubelet/kubectl, ihuan qemu-guest-agent.',
'blog_stack_14' => 'Worker clone automation count-based ihuan idempotent: LAB_PIMOX_WORKER_COUNT=1 quinequi VM 9010 ma onca, ahmo yancuic VM nochipa. Local storage amo quiselia worker clones.',
'blog_stack_15' => 'OpenWrt amo quiza ipan Debian golden-node template. Lab quitemoa ARM SystemReady EFI image, quimporta VM 9050 ipan nvme_thin_pool, ihuan zan quisa quema LAB_OPENWRT_VM=true.',
'blog_stack_16' => 'Monitoring layer quipia Prometheus Stack, Grafana, Mimir, Loki, Promtail, node-exporter, ihuan kube-state-metrics.',
'demos_kicker' => 'Tepiton tools ipan browser',
'demos_title' => 'Demo Apps',