Adding arr stack

This commit is contained in:
juvdiaz 2026-06-22 08:47:35 -06:00
parent a69966c457
commit 3429069b9e
13 changed files with 810 additions and 21 deletions

View File

@ -156,7 +156,7 @@ applies the edge stack.
Set `LAB_PIMOX_PIPELINE=false` to skip Pimox automation. Set Set `LAB_PIMOX_PIPELINE=false` to skip Pimox automation. Set
`LAB_PIMOX_WORKER_COUNT=0` to create or refresh only the template. The pipeline `LAB_PIMOX_WORKER_COUNT=0` to create or refresh only the template. The pipeline
keeps the template on its configured `local` storage, creates new worker VM keeps the template on its configured `local` storage, creates new worker VM
clones on `nvme_thin_pool` by default, checks that the Pimox bridge already clones on `nvme_storage` by default, checks that the Pimox bridge already
exists, refuses `local` as worker clone storage, and refuses to edit Orange Pi exists, refuses `local` as worker clone storage, and refuses to edit Orange Pi
host networking. host networking.
@ -171,7 +171,7 @@ attaches to both WAN and LAN bridges. Run `./lab.sh openwrt` after `vmbr1`
already exists on the Orange Pi. The pipeline downloads the OpenWrt ARM already exists on the Orange Pi. The pipeline downloads the OpenWrt ARM
SystemReady EFI image, writes basic WAN/LAN/firewall config into the image, SystemReady EFI image, writes basic WAN/LAN/firewall config into the image,
imports it as VM `9100`, attaches `vmbr0` as WAN and `vmbr1` as LAN, and stores imports it as VM `9100`, attaches `vmbr0` as WAN and `vmbr1` as LAN, and stores
the VM disk on `nvme_thin_pool`. It leaves the VM stopped and not enabled for the VM disk on `nvme_storage`. It leaves the VM stopped and not enabled for
host boot by default. It does not use the Debian Kubernetes golden-node template host boot by default. It does not use the Debian Kubernetes golden-node template
for OpenWrt. for OpenWrt.
@ -228,7 +228,7 @@ For Pimox on Orange Pi 5 Plus, `./lab.sh up` can create the Debian 13 arm64
template and worker VM clones automatically. Defaults are intentionally tied to template and worker VM clones automatically. Defaults are intentionally tied to
the observed host: Pimox SSH host `192.168.100.80`, bridge `vmbr0`, template VMID the observed host: Pimox SSH host `192.168.100.80`, bridge `vmbr0`, template VMID
`9000` on `local` storage, two 4 GiB worker VMs starting at VMID `9010`, worker `9000` on `local` storage, two 4 GiB worker VMs starting at VMID `9010`, worker
clone storage `nvme_thin_pool`, and no CPU affinity because this Pimox is pinned clone storage `nvme_storage`, and no CPU affinity because this Pimox is pinned
to Debian Bullseye. Details and override variables are in to Debian Bullseye. Details and override variables are in
`bootstrap/provisioning/README.md`. `bootstrap/provisioning/README.md`.
@ -348,6 +348,13 @@ Traefik's address:
192.168.100.240 demos.lab2025.duckdns.org 192.168.100.240 demos.lab2025.duckdns.org
192.168.100.240 heimdall.lab2025.duckdns.org 192.168.100.240 heimdall.lab2025.duckdns.org
192.168.100.240 n8n.lab2025.duckdns.org 192.168.100.240 n8n.lab2025.duckdns.org
192.168.100.240 prowlarr.lab2025.duckdns.org
192.168.100.240 sonarr.lab2025.duckdns.org
192.168.100.240 radarr.lab2025.duckdns.org
192.168.100.240 qbittorrent.lab2025.duckdns.org
192.168.100.240 kapowarr.lab2025.duckdns.org
192.168.100.240 suwayomi.lab2025.duckdns.org
192.168.100.240 maintainerr.lab2025.duckdns.org
192.168.100.240 grafana.lab2025.duckdns.org 192.168.100.240 grafana.lab2025.duckdns.org
192.168.100.240 argocd.lab2025.duckdns.org 192.168.100.240 argocd.lab2025.duckdns.org
``` ```
@ -501,6 +508,12 @@ exposed at `https://n8n.lab2025.duckdns.org`. It receives
`OLLAMA_BASE_URL=http://192.168.100.68:11434` so workflows can call the existing `OLLAMA_BASE_URL=http://192.168.100.68:11434` so workflows can call the existing
Debian-host Ollama API for translation prewarming and batch jobs. Debian-host Ollama API for translation prewarming and batch jobs.
The `arr-stack` app runs Prowlarr, Sonarr, Radarr, qBittorrent, Kapowarr,
Suwayomi, Maintainerr, and FlareSolverr as native Kubernetes Deployments in the
`arr-stack` namespace. It uses retained OpenEBS PVCs for shared `/data` media
paths and per-service config directories, and exposes the user interfaces
through dedicated Traefik hostnames instead of a Tailscale sidecar.
## Storage ## Storage
OpenEBS provides the platform storage provisioner. Stateful Kubernetes apps use OpenEBS provides the platform storage provisioner. Stateful Kubernetes apps use

132
apps/arr-stack/ingress.yaml Normal file
View File

@ -0,0 +1,132 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: prowlarr
namespace: arr-stack
spec:
ingressClassName: traefik
rules:
- host: prowlarr.lab2025.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: prowlarr
port:
number: 9696
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sonarr
namespace: arr-stack
spec:
ingressClassName: traefik
rules:
- host: sonarr.lab2025.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sonarr
port:
number: 8989
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: radarr
namespace: arr-stack
spec:
ingressClassName: traefik
rules:
- host: radarr.lab2025.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: radarr
port:
number: 7878
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: qbittorrent
namespace: arr-stack
spec:
ingressClassName: traefik
rules:
- host: qbittorrent.lab2025.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: qbittorrent
port:
number: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kapowarr
namespace: arr-stack
spec:
ingressClassName: traefik
rules:
- host: kapowarr.lab2025.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kapowarr
port:
number: 5656
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: suwayomi
namespace: arr-stack
spec:
ingressClassName: traefik
rules:
- host: suwayomi.lab2025.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: suwayomi
port:
number: 4567
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: maintainerr
namespace: arr-stack
spec:
ingressClassName: traefik
rules:
- host: maintainerr.lab2025.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: maintainerr
port:
number: 6246

View File

@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- storage.yaml
- web-app.yaml
- ingress.yaml

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: arr-stack

View File

@ -0,0 +1,25 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: arr-config
namespace: arr-stack
spec:
accessModes:
- ReadWriteOnce
storageClassName: openebs-hostpath-retain
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: arr-data
namespace: arr-stack
spec:
accessModes:
- ReadWriteOnce
storageClassName: openebs-hostpath-retain
resources:
requests:
storage: 500Gi

538
apps/arr-stack/web-app.yaml Normal file
View File

@ -0,0 +1,538 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: flaresolverr
namespace: arr-stack
labels:
app: flaresolverr
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: flaresolverr
template:
metadata:
labels:
app: flaresolverr
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- name: flaresolverr
image: ghcr.io/flaresolverr/flaresolverr:latest
imagePullPolicy: IfNotPresent
env:
- name: LOG_LEVEL
value: info
- name: TZ
value: America/Mexico_City
ports:
- containerPort: 8191
name: http
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
memory: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: flaresolverr
namespace: arr-stack
spec:
selector:
app: flaresolverr
ports:
- port: 8191
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prowlarr
namespace: arr-stack
labels:
app: prowlarr
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: prowlarr
template:
metadata:
labels:
app: prowlarr
spec:
nodeSelector:
kubernetes.io/os: linux
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: prowlarr
image: lscr.io/linuxserver/prowlarr:latest
imagePullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: America/Mexico_City
ports:
- containerPort: 9696
name: http
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
volumeMounts:
- name: arr-config
mountPath: /config
subPath: prowlarr
volumes:
- name: arr-config
persistentVolumeClaim:
claimName: arr-config
---
apiVersion: v1
kind: Service
metadata:
name: prowlarr
namespace: arr-stack
spec:
selector:
app: prowlarr
ports:
- port: 9696
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: radarr
namespace: arr-stack
labels:
app: radarr
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: radarr
template:
metadata:
labels:
app: radarr
spec:
nodeSelector:
kubernetes.io/os: linux
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: radarr
image: lscr.io/linuxserver/radarr:latest
imagePullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: America/Mexico_City
ports:
- containerPort: 7878
name: http
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
memory: 2Gi
volumeMounts:
- name: arr-config
mountPath: /config
subPath: radarr
- name: arr-data
mountPath: /data
volumes:
- name: arr-config
persistentVolumeClaim:
claimName: arr-config
- name: arr-data
persistentVolumeClaim:
claimName: arr-data
---
apiVersion: v1
kind: Service
metadata:
name: radarr
namespace: arr-stack
spec:
selector:
app: radarr
ports:
- port: 7878
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarr
namespace: arr-stack
labels:
app: sonarr
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: sonarr
template:
metadata:
labels:
app: sonarr
spec:
nodeSelector:
kubernetes.io/os: linux
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: sonarr
image: lscr.io/linuxserver/sonarr:latest
imagePullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: America/Mexico_City
ports:
- containerPort: 8989
name: http
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
memory: 2Gi
volumeMounts:
- name: arr-config
mountPath: /config
subPath: sonarr
- name: arr-data
mountPath: /data
volumes:
- name: arr-config
persistentVolumeClaim:
claimName: arr-config
- name: arr-data
persistentVolumeClaim:
claimName: arr-data
---
apiVersion: v1
kind: Service
metadata:
name: sonarr
namespace: arr-stack
spec:
selector:
app: sonarr
ports:
- port: 8989
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: qbittorrent
namespace: arr-stack
labels:
app: qbittorrent
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: qbittorrent
template:
metadata:
labels:
app: qbittorrent
spec:
nodeSelector:
kubernetes.io/os: linux
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: qbittorrent
image: lscr.io/linuxserver/qbittorrent:latest
imagePullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: America/Mexico_City
- name: WEBUI_PORT
value: "8080"
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
memory: 2Gi
volumeMounts:
- name: arr-config
mountPath: /config
subPath: qbittorrent
- name: arr-data
mountPath: /data
volumes:
- name: arr-config
persistentVolumeClaim:
claimName: arr-config
- name: arr-data
persistentVolumeClaim:
claimName: arr-data
---
apiVersion: v1
kind: Service
metadata:
name: qbittorrent
namespace: arr-stack
spec:
selector:
app: qbittorrent
ports:
- port: 8080
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kapowarr
namespace: arr-stack
labels:
app: kapowarr
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: kapowarr
template:
metadata:
labels:
app: kapowarr
spec:
nodeSelector:
kubernetes.io/os: linux
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: kapowarr
image: mrcas/kapowarr:latest
imagePullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: America/Mexico_City
ports:
- containerPort: 5656
name: http
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
volumeMounts:
- name: arr-config
mountPath: /app/db
subPath: kapowarr
- name: arr-data
mountPath: /app/temp_downloads
subPath: torrents
- name: arr-data
mountPath: /comics
subPath: media/comics
volumes:
- name: arr-config
persistentVolumeClaim:
claimName: arr-config
- name: arr-data
persistentVolumeClaim:
claimName: arr-data
---
apiVersion: v1
kind: Service
metadata:
name: kapowarr
namespace: arr-stack
spec:
selector:
app: kapowarr
ports:
- port: 5656
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: suwayomi
namespace: arr-stack
labels:
app: suwayomi
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: suwayomi
template:
metadata:
labels:
app: suwayomi
spec:
nodeSelector:
kubernetes.io/os: linux
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: suwayomi
image: ghcr.io/suwayomi/suwayomi-server:preview
imagePullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: America/Mexico_City
ports:
- containerPort: 4567
name: http
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
memory: 2Gi
volumeMounts:
- name: arr-config
mountPath: /home/suwayomi/.local/share/Suwayomi-Server
subPath: suwayomi
- name: arr-data
mountPath: /data/manga
subPath: media/manga
volumes:
- name: arr-config
persistentVolumeClaim:
claimName: arr-config
- name: arr-data
persistentVolumeClaim:
claimName: arr-data
---
apiVersion: v1
kind: Service
metadata:
name: suwayomi
namespace: arr-stack
spec:
selector:
app: suwayomi
ports:
- port: 4567
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: maintainerr
namespace: arr-stack
labels:
app: maintainerr
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: maintainerr
template:
metadata:
labels:
app: maintainerr
spec:
nodeSelector:
kubernetes.io/os: linux
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
- name: maintainerr
image: ghcr.io/jorenn92/maintainerr:latest
imagePullPolicy: IfNotPresent
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: America/Mexico_City
ports:
- containerPort: 6246
name: http
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
volumeMounts:
- name: arr-config
mountPath: /opt/data
subPath: maintainerr
volumes:
- name: arr-config
persistentVolumeClaim:
claimName: arr-config
---
apiVersion: v1
kind: Service
metadata:
name: maintainerr
namespace: arr-stack
spec:
selector:
app: maintainerr
ports:
- port: 6246
targetPort: http

View File

@ -88,6 +88,55 @@ data:
"description": "Workflow automation for translation jobs and homelab tasks", "description": "Workflow automation for translation jobs and homelab tasks",
"colour": "#ea4b71", "colour": "#ea4b71",
"class": "N8N" "class": "N8N"
},
{
"title": "Prowlarr",
"url": "https://prowlarr.lab2025.duckdns.org/",
"description": "Indexer management for the media automation stack",
"colour": "#2563eb",
"class": "Prowlarr"
},
{
"title": "Sonarr",
"url": "https://sonarr.lab2025.duckdns.org/",
"description": "TV library automation",
"colour": "#22c55e",
"class": "Sonarr"
},
{
"title": "Radarr",
"url": "https://radarr.lab2025.duckdns.org/",
"description": "Movie library automation",
"colour": "#f59e0b",
"class": "Radarr"
},
{
"title": "qBittorrent",
"url": "https://qbittorrent.lab2025.duckdns.org/",
"description": "Torrent download client",
"colour": "#3b82f6",
"class": "Qbittorrent"
},
{
"title": "Kapowarr",
"url": "https://kapowarr.lab2025.duckdns.org/",
"description": "Comics library automation",
"colour": "#ef4444",
"class": "Kapowarr"
},
{
"title": "Suwayomi",
"url": "https://suwayomi.lab2025.duckdns.org/",
"description": "Manga server and scraper engine",
"colour": "#8b5cf6",
"class": "Suwayomi"
},
{
"title": "Maintainerr",
"url": "https://maintainerr.lab2025.duckdns.org/",
"description": "Media list maintenance automation",
"colour": "#14b8a6",
"class": "Maintainerr"
} }
] ]
seed.py: | seed.py: |

View File

@ -443,7 +443,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
<rect width="258" height="112" rx="8"></rect> <rect width="258" height="112" rx="8"></rect>
<text x="18" y="28">Orange Pi 5 Plus Pimox</text> <text x="18" y="28">Orange Pi 5 Plus Pimox</text>
<text class="diagram-small" x="18" y="52">pimox-worker app nodes</text> <text class="diagram-small" x="18" y="52">pimox-worker app nodes</text>
<text class="diagram-small" x="18" y="70">workers on nvme_thin_pool</text> <text class="diagram-small" x="18" y="70">workers on nvme_storage</text>
<text class="diagram-small" x="18" y="88">Argo CD, Kyverno, website, n8n</text> <text class="diagram-small" x="18" y="88">Argo CD, Kyverno, website, n8n</text>
<text class="diagram-small" x="18" y="106">Redis cache and app workloads</text> <text class="diagram-small" x="18" y="106">Redis cache and app workloads</text>
</g> </g>

View File

@ -126,14 +126,14 @@ return [
'blog_q7' => 'Can the current cluster actually handle all that, or are we about to smoke the Pi?', '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_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_q8' => 'So the lab can now build its own worker nodes?',
'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_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_storage, so local storage stays reserved for the template.',
'blog_q9' => 'And OpenWrt is joining the story too?', '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_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 and scheduling side?', 'blog_q10' => 'What changed on the observability and scheduling side?',
'blog_a10' => 'Monitoring moved from "someday" to "running," and scheduling moved from "whatever fits" to explicit worker placement. Prometheus Stack, Grafana, Loki, Promtail, node-exporter, and kube-state-metrics give the lab useful signals; the next useful step is choosing the few alerts that would actually wake me up for the right reasons.', 'blog_a10' => 'Monitoring moved from "someday" to "running," and scheduling moved from "whatever fits" to explicit worker placement. Prometheus Stack, Grafana, Loki, Promtail, node-exporter, and kube-state-metrics give the lab useful signals; the next useful step 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_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_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 keeps the external Gitea service close to the lab, while Pimox on the Orange Pi 5 Plus provides the app-worker pool. The Debian template stays on local storage and Kubernetes worker clones go to nvme_thin_pool.', 'blog_stack_2' => 'Raspberry Pi keeps the external Gitea service close to the lab, while Pimox on the Orange Pi 5 Plus provides the app-worker pool. The Debian template stays on local storage and Kubernetes worker clones go to nvme_storage.',
'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_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_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 and now runs on app workers: manifests live in Git, the cluster follows along, and manual drift gets side-eyed back into place.', 'blog_stack_5' => 'Argo CD is the GitOps referee and now runs on app workers: manifests live in Git, the cluster follows along, and manual drift gets side-eyed back into place.',
@ -146,7 +146,7 @@ return [
'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_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_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_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_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_storage, and keeps it disabled unless LAB_OPENWRT_VM=true is set.',
'blog_stack_16' => 'The monitoring layer now includes Prometheus Stack, Grafana, Loki, Promtail, node-exporter, and kube-state-metrics, with placement guardrails so platform add-ons stop defaulting to the control plane.', 'blog_stack_16' => 'The monitoring layer now includes Prometheus Stack, Grafana, Loki, Promtail, node-exporter, and kube-state-metrics, with placement guardrails so platform add-ons stop defaulting to the control plane.',
'blog_stack_17' => 'The portfolio website keeps Apache at the core, runs PHP through PHP-FPM with OPcache, pre-renders read-heavy pages into static HTML during the image build, and leaves PHP for write and translation endpoints.', 'blog_stack_17' => 'The portfolio website keeps Apache at the core, runs PHP through PHP-FPM with OPcache, pre-renders read-heavy pages into static HTML during the image build, and leaves PHP for write and translation endpoints.',
'blog_stack_18' => 'The supply-chain path now uses BuildKit SBOM attestations, Cosign signatures, a cluster-published public key, and an audit-first Kyverno ImageValidatingPolicy that can be promoted to deny unsigned local-registry pods.', 'blog_stack_18' => 'The supply-chain path now uses BuildKit SBOM attestations, Cosign signatures, a cluster-published public key, and an audit-first Kyverno ImageValidatingPolicy that can be promoted to deny unsigned local-registry pods.',
@ -169,7 +169,7 @@ return [
'blog_activity_8' => 'Changed Gitea backups to dump from the Raspberry Pi Docker container and store archives on the Debian host.', 'blog_activity_8' => 'Changed Gitea backups to dump from the Raspberry Pi Docker container and store archives on the Debian host.',
'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_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_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_11' => 'Added NVMe-backed Pimox worker clone automation so VM 9000 stays on local storage while worker nodes are created on nvme_storage.',
'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_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 and moved platform add-ons such as Argo CD, Kyverno, and prometheus-stack work toward app-worker placement instead of treating the control plane as spare capacity.', 'blog_activity_13' => 'Installed the monitoring stack and moved platform add-ons such as Argo CD, Kyverno, and prometheus-stack work toward app-worker placement instead of treating the control plane as spare capacity.',
'blog_activity_14' => 'Reworked the website image to keep Apache as the web server while moving PHP execution to PHP-FPM, enabling OPcache, and validating the runtime through Gitea Actions instead of local image builds.', 'blog_activity_14' => 'Reworked the website image to keep Apache as the web server while moving PHP execution to PHP-FPM, enabling OPcache, and validating the runtime through Gitea Actions instead of local image builds.',
@ -188,7 +188,7 @@ return [
'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_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 external Gitea and OpenEBS volumes, then write the exact restore runbook.', 'blog_todo_9' => 'Schedule backup restore drills for external 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_10' => 'Tighten TLS, SSH, and token rotation around the OCI edge, Gitea, registry, and runner credentials.',
'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_11' => 'Document the new storage split: local for the Pimox template, nvme_storage 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_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_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_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.',

View File

@ -126,14 +126,14 @@ return [
'blog_q7' => 'Current cluster huel quipia nochi inin, o tiquchichinos Pi?', 'blog_q7' => 'Current cluster huel quipia nochi inin, o tiquchichinos Pi?',
'blog_a7' => 'Pi survives pampa demos cateh intentionally local-first ihuan now ship as separate static artifact. Website pod mocahua portfolio shell, demos-static pod serves static bundles, ihuan browser quichihua expensive work. Tla later onicship real ONNX object detection, Transformers.js, o full video transcoding models, monequi lazy-load ipan browser o yasque beefier node.', 'blog_a7' => 'Pi survives pampa demos cateh intentionally local-first ihuan now ship as separate static artifact. Website pod mocahua portfolio shell, demos-static pod serves static bundles, ihuan browser quichihua expensive work. Tla later onicship real ONNX object detection, Transformers.js, o full video transcoding models, monequi lazy-load ipan browser o yasque beefier node.',
'blog_q8' => 'Axcan lab huel quichihua own worker nodes?', 'blog_q8' => 'Axcan lab huel quichihua own worker nodes?',
'blog_a8' => 'Quena, ihuan axcan ika fewer crossed fingers. Debian quipia provisioning layer ika dnsmasq, nginx, PXE boot files, GRUB, ihuan Debian 13 arm64 preseed. OpenTofu notza Pimox through qm, quichihua VM 9000 ipan local storage, quiboota from network, quinstala OS, quichihua golden-node prep, disables swap, verifies cgroups, installs containerd ihuan kubeadm tooling, then seals it as template. Worker clones cateh idempotent by VMID ihuan now land on nvme_thin_pool.', 'blog_a8' => 'Quena, ihuan axcan ika fewer crossed fingers. Debian quipia provisioning layer ika dnsmasq, nginx, PXE boot files, GRUB, ihuan Debian 13 arm64 preseed. OpenTofu notza Pimox through qm, quichihua VM 9000 ipan local storage, quiboota from network, quinstala OS, quichihua golden-node prep, disables swap, verifies cgroups, installs containerd ihuan kubeadm tooling, then seals it as template. Worker clones cateh idempotent by VMID ihuan now land on nvme_storage.',
'blog_q9' => 'Ihuan OpenWrt no quisa nican?', 'blog_q9' => 'Ihuan OpenWrt no quisa nican?',
'blog_a9' => 'Zan simple firewall, ahmo networking science project. Pipeline huel quichihua opt-in OpenWrt ARM SystemReady VM, attach vmbr0 quen WAN ihuan vmbr1 quen LAN, ihuan configure LAN side ahmo rewriting Orange Pi host networking. DHCP stays optional, VLANs wait until onca managed switch ihuan local test window.', 'blog_a9' => 'Zan simple firewall, ahmo networking science project. Pipeline huel quichihua opt-in OpenWrt ARM SystemReady VM, attach vmbr0 quen WAN ihuan vmbr1 quen LAN, ihuan configure LAN side ahmo rewriting Orange Pi host networking. DHCP stays optional, VLANs wait until onca managed switch ihuan local test window.',
'blog_q10' => 'Tlein omopatla ipan observability ihuan scheduling side?', 'blog_q10' => 'Tlein omopatla ipan observability ihuan scheduling side?',
'blog_a10' => 'Monitoring moved de "someday" to "running," ihuan scheduling moved de "whatever fits" to explicit worker placement. Prometheus Stack, Grafana, Loki, Promtail, node-exporter, ihuan kube-state-metrics quimaca useful signals; next step quitosnequi elegir few alerts in melahuac nechixitisque para right reasons.', 'blog_a10' => 'Monitoring moved de "someday" to "running," ihuan scheduling moved de "whatever fits" to explicit worker placement. Prometheus Stack, Grafana, Loki, Promtail, node-exporter, ihuan kube-state-metrics quimaca useful signals; next step quitosnequi elegir few alerts in melahuac nechixitisque para right reasons.',
'blog_stack_title' => 'Tlamantli ihuan tleica nemi nican', 'blog_stack_title' => 'Tlamantli ihuan tleica nemi nican',
'blog_stack_1' => 'Debian Linux quimaca steady adult in room: control plane host, deployment workstation, PXE/preseed server, ihuan canin OpenTofu, Docker, kubeadm, ihuan scripts quichihua tequitl.', 'blog_stack_1' => 'Debian Linux quimaca steady adult in room: control plane host, deployment workstation, PXE/preseed server, ihuan canin OpenTofu, Docker, kubeadm, ihuan scripts quichihua tequitl.',
'blog_stack_2' => 'Raspberry Pi keeps external Gitea service close to lab, while Pimox ipan Orange Pi 5 Plus provides app-worker pool. Debian template mocahua ipan local storage ihuan Kubernetes worker clones yahui ipan nvme_thin_pool.', 'blog_stack_2' => 'Raspberry Pi keeps external Gitea service close to lab, while Pimox ipan Orange Pi 5 Plus provides app-worker pool. Debian template mocahua ipan local storage ihuan Kubernetes worker clones yahui ipan nvme_storage.',
'blog_stack_3' => 'OpenTofu quichihua cluster, platform, apps, edge, ihuan provisioning layers repeatable, pampa "I swear I remember the command" amo disaster recovery strategy.', 'blog_stack_3' => 'OpenTofu quichihua cluster, platform, apps, edge, ihuan provisioning layers repeatable, pampa "I swear I remember the command" amo disaster recovery strategy.',
'blog_stack_4' => 'Calico handles pod networking, ihuan OpenEBS hostpath storage keeps important data after rebuilds, pampa deleting everything by accident zan funny once.', 'blog_stack_4' => 'Calico handles pod networking, ihuan OpenEBS hostpath storage keeps important data after rebuilds, pampa deleting everything by accident zan funny once.',
'blog_stack_5' => 'Argo CD quimaca GitOps referee ihuan now runs on app workers: manifests cateh ipan Git, cluster follows along, ihuan manual drift gets side-eyed back into place.', 'blog_stack_5' => 'Argo CD quimaca GitOps referee ihuan now runs on app workers: manifests cateh ipan Git, cluster follows along, ihuan manual drift gets side-eyed back into place.',
@ -146,7 +146,7 @@ return [
'blog_stack_12' => 'Pimox worker pipeline uses qm over SSH para create OVMF/virtio-scsi Debian 13 arm64 VM, wait for qemu-guest-agent, seal it, ihuan convert VM 9000 into reusable template on local storage.', 'blog_stack_12' => 'Pimox worker pipeline uses qm over SSH para create OVMF/virtio-scsi Debian 13 arm64 VM, wait for qemu-guest-agent, seal it, ihuan convert VM 9000 into reusable template on local storage.',
'blog_stack_13' => 'Golden image bakes in Kubernetes prerequisites: swap disabled, cgroup boot options checked, kernel modules loaded, containerd configured for systemd cgroups, kubeadm/kubelet/kubectl installed, ihuan qemu-guest-agent enabled.', 'blog_stack_13' => 'Golden image bakes in Kubernetes prerequisites: swap disabled, cgroup boot options checked, kernel modules loaded, containerd configured for systemd cgroups, kubeadm/kubelet/kubectl installed, ihuan qemu-guest-agent enabled.',
'blog_stack_14' => 'Worker clone automation is count-based ihuan idempotent: LAB_PIMOX_WORKER_COUNT=1 quitosnequi ensure VM 9010 exists, amo create fresh worker every run. New workers refused tla target storage is local.', 'blog_stack_14' => 'Worker clone automation is count-based ihuan idempotent: LAB_PIMOX_WORKER_COUNT=1 quitosnequi ensure VM 9010 exists, amo create fresh worker every run. New workers refused tla target storage is local.',
'blog_stack_15' => 'OpenWrt handled separately from Debian golden-node template. Lab downloads upstream ARM SystemReady EFI image, imports it as VM 9050 on nvme_thin_pool, ihuan keeps disabled unless LAB_OPENWRT_VM=true.', 'blog_stack_15' => 'OpenWrt handled separately from Debian golden-node template. Lab downloads upstream ARM SystemReady EFI image, imports it as VM 9050 on nvme_storage, ihuan keeps disabled unless LAB_OPENWRT_VM=true.',
'blog_stack_16' => 'Monitoring layer axcan includes Prometheus Stack, Grafana, Loki, Promtail, node-exporter, ihuan kube-state-metrics, ika placement guardrails para platform add-ons ma amo default to control plane.', 'blog_stack_16' => 'Monitoring layer axcan includes Prometheus Stack, Grafana, Loki, Promtail, node-exporter, ihuan kube-state-metrics, ika placement guardrails para platform add-ons ma amo default to control plane.',
'blog_stack_17' => 'Portfolio website keeps Apache at core, runs PHP through PHP-FPM ika OPcache, pre-renders read-heavy pages into static HTML during image build, ihuan leaves PHP para write ihuan translation endpoints.', 'blog_stack_17' => 'Portfolio website keeps Apache at core, runs PHP through PHP-FPM ika OPcache, pre-renders read-heavy pages into static HTML during image build, ihuan leaves PHP para write ihuan translation endpoints.',
'blog_stack_18' => 'Supply-chain path axcan uses BuildKit SBOM attestations, Cosign signatures, public key ipan cluster, ihuan audit-first Kyverno ImageValidatingPolicy in huel mopromote para deny unsigned local-registry pods.', 'blog_stack_18' => 'Supply-chain path axcan uses BuildKit SBOM attestations, Cosign signatures, public key ipan cluster, ihuan audit-first Kyverno ImageValidatingPolicy in huel mopromote para deny unsigned local-registry pods.',
@ -169,7 +169,7 @@ return [
'blog_activity_8' => 'Changed Gitea backups to dump from Raspberry Pi Docker container ihuan store archives on Debian host.', 'blog_activity_8' => 'Changed Gitea backups to dump from Raspberry Pi Docker container ihuan store archives on Debian host.',
'blog_activity_9' => 'Validated full main-branch deployment path: fetch main, apply OpenTofu layers, build ihuan push arm64 images, refresh Argo CD, ihuan confirm runner completes successfully.', 'blog_activity_9' => 'Validated full main-branch deployment path: fetch main, apply OpenTofu layers, build ihuan push arm64 images, refresh Argo CD, ihuan confirm runner completes successfully.',
'blog_activity_10' => 'Built Debian 13 arm64 Pimox template end to end ika PXE, preseed, qemu-guest-agent discovery, cgroup validation, swap disabled, ihuan final seal step.', 'blog_activity_10' => 'Built Debian 13 arm64 Pimox template end to end ika PXE, preseed, qemu-guest-agent discovery, cgroup validation, swap disabled, ihuan final seal step.',
'blog_activity_11' => 'Added NVMe-backed Pimox worker clone automation para VM 9000 ma mocahua ipan local storage mientras worker nodes mochihuah ipan nvme_thin_pool.', 'blog_activity_11' => 'Added NVMe-backed Pimox worker clone automation para VM 9000 ma mocahua ipan local storage mientras worker nodes mochihuah ipan nvme_storage.',
'blog_activity_12' => 'Added opt-in OpenWrt VM path para simple firewall between vmbr0 ihuan vmbr1, ika guardrails in avoid Orange Pi host networking changes.', 'blog_activity_12' => 'Added opt-in OpenWrt VM path para simple firewall between vmbr0 ihuan vmbr1, ika guardrails in avoid Orange Pi host networking changes.',
'blog_activity_13' => 'Installed monitoring stack ihuan moved platform add-ons such as Argo CD, Kyverno, ihuan prometheus-stack toward app-worker placement instead of treating control plane as spare capacity.', 'blog_activity_13' => 'Installed monitoring stack ihuan moved platform add-ons such as Argo CD, Kyverno, ihuan prometheus-stack toward app-worker placement instead of treating control plane as spare capacity.',
'blog_activity_14' => 'Reworked website image para Apache ma mocahua web server while PHP runs through PHP-FPM, OPcache enabled, ihuan runtime validated through Gitea Actions instead of local image builds.', 'blog_activity_14' => 'Reworked website image para Apache ma mocahua web server while PHP runs through PHP-FPM, OPcache enabled, ihuan runtime validated through Gitea Actions instead of local image builds.',
@ -188,7 +188,7 @@ return [
'blog_todo_8' => 'Turn installed observability stack into useful operations views: few high-signal dashboards, alerts para node health, storage pressure, certificate expiry, ihuan failed app syncs.', 'blog_todo_8' => 'Turn installed observability stack into useful operations views: few high-signal dashboards, alerts para node health, storage pressure, certificate expiry, ihuan failed app syncs.',
'blog_todo_9' => 'Schedule backup restore drills para external Gitea ihuan OpenEBS volumes, then write exact restore runbook.', 'blog_todo_9' => 'Schedule backup restore drills para external Gitea ihuan OpenEBS volumes, then write exact restore runbook.',
'blog_todo_10' => 'Tighten TLS, SSH, ihuan token rotation around OCI edge, Gitea, registry, ihuan runner credentials.', 'blog_todo_10' => 'Tighten TLS, SSH, ihuan token rotation around OCI edge, Gitea, registry, ihuan runner credentials.',
'blog_todo_11' => 'Document new storage split: local para Pimox template, nvme_thin_pool para VM workers, OpenEBS para Kubernetes app data, ihuan backup targets para tlein must survive rebuild.', 'blog_todo_11' => 'Document new storage split: local para Pimox template, nvme_storage para VM workers, OpenEBS para Kubernetes app data, ihuan backup targets para tlein must survive rebuild.',
'blog_todo_12' => 'Move sensitive app configuration into Sealed Secrets, External Secrets, o occe explicit secret-management path.', 'blog_todo_12' => 'Move sensitive app configuration into Sealed Secrets, External Secrets, o occe explicit secret-management path.',
'blog_todo_13' => 'After new disk has breathing room, clone first Pimox worker from VM 9000, join it ika kubeadm, ihuan verify labels, taints, CNI, storage, ihuan workload scheduling.', 'blog_todo_13' => 'After new disk has breathing room, clone first Pimox worker from VM 9000, join it ika kubeadm, ihuan verify labels, taints, CNI, storage, ihuan workload scheduling.',
'blog_todo_14' => 'Test OpenWrt VM ipan maintenance window before making it gateway: confirm WAN, LAN, rollback access, DHCP settings, ihuan Pimox remains reachable.', 'blog_todo_14' => 'Test OpenWrt VM ipan maintenance window before making it gateway: confirm WAN, LAN, rollback access, DHCP settings, ihuan Pimox remains reachable.',

View File

@ -75,6 +75,15 @@ variable "applications" {
self_heal = true self_heal = true
create_namespace = true create_namespace = true
} }
arr-stack = {
project = "default"
path = "apps/arr-stack"
namespace = "arr-stack"
target_revision = "main"
prune = true
self_heal = true
create_namespace = true
}
supply-chain-policy = { supply-chain-policy = {
project = "default" project = "default"
path = "apps/supply-chain-policy" path = "apps/supply-chain-policy"

View File

@ -112,7 +112,7 @@ networking, virtio-scsi disk, `vmbr0`, `local` template storage, 1 socket with
`./lab.sh up` also creates or reuses worker clones after the template exists. It `./lab.sh up` also creates or reuses worker clones after the template exists. It
defaults to one worker, VMID `9010`, name `pimox-worker-01`, deterministic defaults to one worker, VMID `9010`, name `pimox-worker-01`, deterministic
locally administered MAC address, 1 socket locally administered MAC address, 1 socket
with 2 cores, 4 GiB RAM, `nvme_thin_pool` clone storage, and qemu-guest-agent IP with 2 cores, 4 GiB RAM, `nvme_storage` clone storage, and qemu-guest-agent IP
discovery. New workers are full clones created with discovery. New workers are full clones created with
`qm clone --storage`, so the template can remain on `local` while worker disks `qm clone --storage`, so the template can remain on `local` while worker disks
land on the NVMe thin pool. Set `LAB_PIMOX_WORKER_REPLACE_EXISTING=true` to land on the NVMe thin pool. Set `LAB_PIMOX_WORKER_REPLACE_EXISTING=true` to
@ -136,7 +136,7 @@ LAB_PIMOX_WORKER_COUNT=0 ./lab.sh up
LAB_PIMOX_WORKER_COUNT=2 ./lab.sh up LAB_PIMOX_WORKER_COUNT=2 ./lab.sh up
LAB_PIMOX_SKIP_WORKER_INDEXES=1 LAB_PIMOX_WORKER_COUNT=2 ./lab.sh up LAB_PIMOX_SKIP_WORKER_INDEXES=1 LAB_PIMOX_WORKER_COUNT=2 ./lab.sh up
LAB_PIMOX_WORKER_BASE_VMID=9020 ./lab.sh up LAB_PIMOX_WORKER_BASE_VMID=9020 ./lab.sh up
LAB_PIMOX_WORKER_STORAGE=nvme_thin_pool ./lab.sh up LAB_PIMOX_WORKER_STORAGE=nvme_storage ./lab.sh up
LAB_PIMOX_WORKER_REPLACE_EXISTING=true ./lab.sh up LAB_PIMOX_WORKER_REPLACE_EXISTING=true ./lab.sh up
LAB_PIMOX_HOST=192.168.100.80 LAB_PIMOX_BRIDGE=vmbr0 ./lab.sh up LAB_PIMOX_HOST=192.168.100.80 LAB_PIMOX_BRIDGE=vmbr0 ./lab.sh up
``` ```
@ -159,7 +159,7 @@ Defaults:
- VMID `9100` - VMID `9100`
- VM name `openwrt-firewall` - VM name `openwrt-firewall`
- disk storage `nvme_thin_pool` - disk storage `nvme_storage`
- WAN bridge `vmbr0` - WAN bridge `vmbr0`
- LAN bridge `vmbr1` - LAN bridge `vmbr1`
- LAN address `192.168.50.1/24` - LAN address `192.168.50.1/24`
@ -172,7 +172,7 @@ Useful overrides:
```bash ```bash
LAB_OPENWRT_VMID=9100 LAB_OPENWRT_VMID=9100
LAB_OPENWRT_STORAGE=nvme_thin_pool LAB_OPENWRT_STORAGE=nvme_storage
LAB_OPENWRT_WAN_BRIDGE=vmbr0 LAB_OPENWRT_WAN_BRIDGE=vmbr0
LAB_OPENWRT_LAN_BRIDGE=vmbr1 LAB_OPENWRT_LAN_BRIDGE=vmbr1
LAB_OPENWRT_LAN_IP=192.168.50.1 LAB_OPENWRT_LAN_IP=192.168.50.1
@ -184,6 +184,6 @@ LAB_OPENWRT_VERSION=24.10.6
LAB_OPENWRT_IMAGE_URL=https://downloads.openwrt.org/releases/24.10.6/targets/armsr/armv8/openwrt-24.10.6-armsr-armv8-generic-ext4-combined-efi.img.gz LAB_OPENWRT_IMAGE_URL=https://downloads.openwrt.org/releases/24.10.6/targets/armsr/armv8/openwrt-24.10.6-armsr-armv8-generic-ext4-combined-efi.img.gz
``` ```
The pipeline validates `vmbr0`, `vmbr1`, and `nvme_thin_pool` on the Pimox host. The pipeline validates `vmbr0`, `vmbr1`, and `nvme_storage` on the Pimox host.
It refuses `local` as OpenWrt storage and refuses to create or modify host It refuses `local` as OpenWrt storage and refuses to create or modify host
network bridges. network bridges.

16
lab.sh
View File

@ -894,7 +894,7 @@ run_pimox_pipeline() {
local worker_memory="${LAB_PIMOX_WORKER_MEMORY:-4096}" local worker_memory="${LAB_PIMOX_WORKER_MEMORY:-4096}"
local worker_cpu_affinities="${LAB_PIMOX_WORKER_CPU_AFFINITIES:-}" local worker_cpu_affinities="${LAB_PIMOX_WORKER_CPU_AFFINITIES:-}"
local worker_replace_existing="${LAB_PIMOX_WORKER_REPLACE_EXISTING:-false}" local worker_replace_existing="${LAB_PIMOX_WORKER_REPLACE_EXISTING:-false}"
local worker_storage="${LAB_PIMOX_WORKER_STORAGE:-${TF_VAR_pimox_worker_storage:-nvme_thin_pool}}" local worker_storage="${LAB_PIMOX_WORKER_STORAGE:-${TF_VAR_pimox_worker_storage:-nvme_storage}}"
local worker_user="${LAB_PIMOX_WORKER_USER:-jv}" local worker_user="${LAB_PIMOX_WORKER_USER:-jv}"
local worker_key_path="${LAB_PIMOX_WORKER_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}" local worker_key_path="${LAB_PIMOX_WORKER_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}"
local ip_prefix="${LAB_PIMOX_GUEST_IP_PREFIX:-192.168.100.}" local ip_prefix="${LAB_PIMOX_GUEST_IP_PREFIX:-192.168.100.}"
@ -1054,7 +1054,7 @@ run_openwrt_pipeline() {
local qm_bin="${LAB_PIMOX_QM_BIN:-${TF_VAR_pimox_qm_bin:-/usr/sbin/qm}}" local qm_bin="${LAB_PIMOX_QM_BIN:-${TF_VAR_pimox_qm_bin:-/usr/sbin/qm}}"
local vmid="${LAB_OPENWRT_VMID:-9100}" local vmid="${LAB_OPENWRT_VMID:-9100}"
local vm_name="${LAB_OPENWRT_NAME:-openwrt-firewall}" local vm_name="${LAB_OPENWRT_NAME:-openwrt-firewall}"
local storage="${LAB_OPENWRT_STORAGE:-nvme_thin_pool}" local storage="${LAB_OPENWRT_STORAGE:-nvme_storage}"
local wan_bridge="${LAB_OPENWRT_WAN_BRIDGE:-vmbr0}" local wan_bridge="${LAB_OPENWRT_WAN_BRIDGE:-vmbr0}"
local lan_bridge="${LAB_OPENWRT_LAN_BRIDGE:-vmbr1}" local lan_bridge="${LAB_OPENWRT_LAN_BRIDGE:-vmbr1}"
local cores="${LAB_OPENWRT_CORES:-2}" local cores="${LAB_OPENWRT_CORES:-2}"
@ -3107,6 +3107,18 @@ apps() {
wait_for_namespaced_resource n8n deployment n8n n8n 300 wait_for_namespaced_resource n8n deployment n8n n8n 300
wait_for_deployment_ready n8n n8n n8n 300 wait_for_deployment_ready n8n n8n n8n 300
refresh_argocd_application arr-stack
wait_for_namespace arr-stack arr-stack 300
wait_for_namespaced_resource arr-stack deployment prowlarr arr-stack 300
wait_for_deployment_ready arr-stack flaresolverr arr-stack 300
wait_for_deployment_ready arr-stack prowlarr arr-stack 300
wait_for_deployment_ready arr-stack radarr arr-stack 300
wait_for_deployment_ready arr-stack sonarr arr-stack 300
wait_for_deployment_ready arr-stack qbittorrent arr-stack 300
wait_for_deployment_ready arr-stack kapowarr arr-stack 300
wait_for_deployment_ready arr-stack suwayomi arr-stack 300
wait_for_deployment_ready arr-stack maintainerr arr-stack 300
echo "Application deployment successfully completed." echo "Application deployment successfully completed."
} }