# helmfile.yaml # Single source of truth for all Helm releases in the homelab cluster. # # Usage: # helmfile apply # deploy / reconcile everything # helmfile apply -l namespace=logging # only logging stack # helmfile apply -l name=grafana # only grafana # helmfile diff # preview changes # helmfile destroy -l name=portainer # tear down one release # # Required env vars (export or place in .env at repo root): # MINIO_ROOT_USER, MINIO_ROOT_PASSWORD # GRAFANA_ADMIN_PASSWORD # # .env is loaded automatically by helmfile if it exists at the repo root. repositories: - name: grafana url: https://grafana.github.io/helm-charts - name: minio url: https://charts.min.io - name: prometheus-community url: https://prometheus-community.github.io/helm-charts - name: portainer url: https://portainer.github.io/k8s/ - name: authentik url: https://charts.goauthentik.io - name: hashicorp url: https://helm.releases.hashicorp.com - name: ingress-nginx url: https://kubernetes.github.io/ingress-nginx - name: argo url: https://argoproj.github.io/argo-helm - name: gitea-charts url: https://dl.gitea.com/charts/ - name: jetstack url: https://charts.jetstack.io - name: stakater url: https://stakater.github.io/stakater-charts - name: strimzi url: https://strimzi.io/charts/ - name: bitnami url: https://charts.bitnami.com/bitnami - name: temporal url: https://go.temporal.io/helm-charts - name: cnpg url: https://cloudnative-pg.github.io/charts # ── cert-manager ───────────────────────────────────────────────────────────── releases: - name: cert-manager namespace: cert-manager createNamespace: true chart: jetstack/cert-manager version: "~v1" values: - k8s/cert-manager/cert-manager-values.yaml set: # CRDs must be installed by the chart — avoids a separate kubectl apply step - name: crds.enabled value: true hooks: - events: ["postsync"] command: bash args: - -c - | # Wait for cert-manager webhooks to be ready before applying CRD instances. # Without this, ClusterIssuer/Certificate creation races the webhook and fails. kubectl rollout status deploy/cert-manager -n cert-manager --timeout=120s kubectl rollout status deploy/cert-manager-webhook -n cert-manager --timeout=120s kubectl apply -f - <<'EOF' # Phase 2a — bootstrap issuer (selfSigned) used only to sign the CA cert. # Never referenced by ingresses — its sole job is to sign homelab-ca below. apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: selfsigned-bootstrap spec: selfSigned: {} --- # Phase 2b — the cluster CA certificate. # cert-manager stores the signed cert + key in homelab-ca-secret. # isCA: true marks it so it can sign other certs. # 10-year lifetime; renewBefore triggers 30 days early. apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: homelab-ca namespace: cert-manager spec: isCA: true commonName: homelab-ca secretName: homelab-ca-secret duration: 87600h renewBefore: 720h privateKey: algorithm: ECDSA size: 256 issuerRef: name: selfsigned-bootstrap kind: ClusterIssuer group: cert-manager.io --- # Phase 2c — the real issuer all ingresses reference. # Reads the CA cert+key from homelab-ca-secret and signs per-hostname certs. # Annotate any ingress with: cert-manager.io/cluster-issuer: homelab-ca apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: homelab-ca spec: ca: secretName: homelab-ca-secret EOF # ── Stakater Reloader ───────────────────────────────────────────────────────── # Watches Deployments for secret.reloader.stakater.com/reload and # configmap.reloader.stakater.com/reload annotations, then triggers a rolling # restart whenever the named Secret or ConfigMap is updated (e.g. cert-manager # renewing a TLS cert, or homelab-ca rotating). This replaces the need for # manual `kubectl rollout restart` on cert renewal. - name: reloader namespace: reloader createNamespace: true chart: stakater/reloader version: "~1" # Pod Disruption Budgets applied after reloader (ensures all namespaces exist) hooks: - events: ["postsync"] command: bash args: - -c - kubectl apply -f k8s/base/poddisruptionbudgets.yaml # ── Ingress ─────────────────────────────────────────────────────────────────── - name: ingress-nginx namespace: ingress-nginx createNamespace: true chart: ingress-nginx/ingress-nginx values: - k8s/ingress/nginx-values.yaml # ServiceMonitor enabled in nginx-values.yaml requires the Prometheus Operator # CRDs, which the prometheus release installs — must apply after it. needs: - monitoring/prometheus hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh ingress-nginx # LB-IPAM pool must exist before any LoadBalancer service is created, # otherwise services stay . Apply it here as the first hook. kubectl apply -f k8s/cilium/lb-ipam-pool.yaml kubectl apply -f k8s/coredns/coredns-configmap.yaml kubectl rollout restart deployment/coredns -n kube-system kubectl rollout status deployment/coredns -n kube-system --timeout=60s # Wildcard TLS cert — must exist before nginx starts so it can read the secret. # cert-manager issues it in the ingress-nginx namespace; wait until Ready. kubectl apply -f k8s/ingress/wildcard-cert.yaml kubectl wait certificate homelab-tls -n ingress-nginx \ --for=condition=Ready --timeout=120s - events: ["postsync"] command: kubectl args: - apply - -f - k8s/ingress/ingress.yaml # ── CloudNativePG (centralized database) ────────────────────────────────────── # Single HA cluster (1 primary + 2 replicas) serving Authentik + story-crater-backend. # pgvector extension enabled for story-crater's canon-graph/draft services. - name: cloudnative-pg namespace: ddb createNamespace: true chart: cnpg/cloudnative-pg version: "~0.20" values: - k8s/ddb/cnpg-values.yaml needs: - ingress-nginx/ingress-nginx # wait for ingress-nginx to be ready hooks: - events: ["postsync"] command: bash args: - -c - | # Wait for operator to be ready kubectl rollout status deploy/cloudnative-pg -n ddb --timeout=120s # Apply the CNPG Cluster CR with pgvector support # Note: Using unquoted EOF so bash can expand variables kubectl apply -f - </dev/null || true kubectl wait pod -n ddb -l cnpg.io/cluster=ddb-cluster --for=condition=Ready --timeout=300s 2>/dev/null || true # Load environment from .env file (safe variable expansion) set -a [ -f .env ] && source .env || true set +a # Initialize database users using external script bash k8s/ddb/init-users.sh # Apply Authentik migrations Job from external manifest kubectl apply -f k8s/talos-iam/authentik-migrations-job.yaml # Wait for migrations Job to complete before proceeding kubectl wait --for=condition=complete job/authentik-migrations -n iam --timeout=300s 2>/dev/null || true # ── IAM (Authentik + Vault) ─────────────────────────────────────────────────── - name: authentik namespace: iam createNamespace: true chart: authentik/authentik values: - k8s/talos-iam/authentik-values.yaml # server/worker metrics.serviceMonitor.enabled requires the Prometheus Operator CRDs. # forgejo and argocd (which `need: iam/authentik`) transitively wait for this too. needs: - monitoring/prometheus set: # Secrets injected at deploy time — never stored in values files or git - name: authentik.secret_key value: {{ env "AUTHENTIK_SECRET_KEY" }} - name: authentik.bootstrap_password value: {{ env "AUTHENTIK_BOOTSTRAP_PASSWORD" }} - name: authentik.bootstrap_token value: {{ env "AUTHENTIK_BOOTSTRAP_TOKEN" }} # Both keys must match — authentik.postgresql.password is what the app # uses to connect; postgresql.auth.password is what the Bitnami subchart # sets on the PostgreSQL user. If they diverge the app can't log in. - name: authentik.postgresql.password value: {{ env "AUTHENTIK_PG_PASSWORD" }} - name: postgresql.auth.password value: {{ env "AUTHENTIK_PG_PASSWORD" }} hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh iam # Read the CA cert from the cert-manager source-of-truth secret so the # ConfigMap always matches what cert-manager actually issued — no hardcoded PEM. CA_PEM=$(kubectl get secret homelab-ca-secret -n cert-manager \ -o jsonpath='{.data.tls\.crt}' | base64 -d) kubectl create configmap homelab-ca -n iam \ --from-literal=homelab-ca.crt="$CA_PEM" \ --dry-run=client -o yaml | kubectl apply -f - - events: ["postsync"] command: bash args: - -c - | # Wait for Authentik server to be ready kubectl rollout status deploy/authentik-server -n iam --timeout=180s # Copy provision script into Authentik pod and execute AUTHENTIK_POD=$(kubectl get pods -n iam -l app.kubernetes.io/name=authentik -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) if [ -n "$AUTHENTIK_POD" ]; then kubectl cp k8s/talos-iam/provision_oidc.py iam/$AUTHENTIK_POD:/tmp/provision_oidc.py -c server 2>/dev/null || true kubectl exec -n iam $AUTHENTIK_POD -c server -- python3 /tmp/provision_oidc.py \ --authentik-url http://localhost:9000 \ --bootstrap-token {{ env "AUTHENTIK_BOOTSTRAP_TOKEN" }} 2>/dev/null || echo "OIDC provisioning completed" fi - name: vault namespace: iam chart: hashicorp/vault values: - k8s/talos-iam/vault-values.yaml needs: - storage/minio hooks: - events: ["presync"] command: bash args: - -c - | # MinIO creds for the S3 storage backend kubectl create secret generic vault-minio-creds -n iam \ --from-literal=access_key="{{ env "MINIO_ROOT_USER" }}" \ --from-literal=secret_key="{{ env "MINIO_ROOT_PASSWORD" }}" \ --dry-run=client -o yaml | kubectl apply -f - # Placeholder unseal keys — setup_vault.sh overwrites with real values after init kubectl get secret vault-unseal-keys -n iam >/dev/null 2>&1 || \ kubectl create secret generic vault-unseal-keys -n iam \ --from-literal=key1="" \ --from-literal=key2="" \ --from-literal=key3="" # Ensure vault bucket exists in MinIO before Vault starts (credentials in Secret, not env) bash k8s/storage/minio-bucket-init.sh iam vault # ── Storage (MinIO with Longhorn replication) ─────────────────────────────── - name: minio namespace: storage createNamespace: true chart: minio/minio values: - k8s/storage/minio-values.yaml set: - name: rootUser value: {{ env "MINIO_ROOT_USER" }} - name: rootPassword value: {{ env "MINIO_ROOT_PASSWORD" }} hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh storage CA_PEM=$(kubectl get secret homelab-ca-secret -n cert-manager \ -o jsonpath='{.data.tls\.crt}' | base64 -d) kubectl create configmap homelab-ca -n storage \ --from-literal=homelab-ca.crt="$CA_PEM" \ --dry-run=client -o yaml | kubectl apply -f - kubectl create secret generic minio-oidc -n storage \ --from-literal=MINIO_IDENTITY_OPENID_CLIENT_SECRET="{{ env "MINIO_OIDC_CLIENT_SECRET" }}" \ --dry-run=client -o yaml | kubectl apply -f - bash k8s/storage/minio-bucket-init.sh storage loki-chunks loki-ruler loki-admin loki-index vault riotpiao-models # ── Logging (Loki + Promtail + Grafana — backed by storage/minio) ─── # Loki uses minio.storage.svc.cluster.local:9000 with Longhorn-replicated storage. # Buckets (loki-chunks, loki-ruler, loki-admin) created by minio presync hook. - name: loki namespace: logging createNamespace: true chart: grafana/loki values: - k8s/logging/loki-values.yaml set: - name: loki.storage.s3.accessKeyId value: {{ env "MINIO_ROOT_USER" }} - name: loki.storage.s3.secretAccessKey value: {{ env "MINIO_ROOT_PASSWORD" }} - name: loki.storage.bucketNames.chunks value: loki-chunks - name: loki.storage.bucketNames.ruler value: loki-ruler - name: loki.storage.bucketNames.admin value: loki-admin hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh logging # Pre-create buckets BEFORE Loki pod starts — prevents NoSuchBucket errors bash k8s/storage/minio-bucket-init.sh storage loki-chunks loki-ruler loki-admin needs: - storage/minio - name: promtail namespace: logging chart: grafana/promtail values: - k8s/logging/promtail-values.yaml needs: - logging/loki - name: grafana namespace: logging chart: grafana/grafana values: - k8s/logging/grafana-values.yaml set: - name: adminPassword value: {{ env "GRAFANA_ADMIN_PASSWORD" }} - name: env.GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET value: {{ env "GRAFANA_OIDC_CLIENT_SECRET" }} needs: - logging/loki hooks: - events: ["postsync"] command: kubectl args: - apply - -f - k8s/monitoring/dashboards/ # ── Monitoring (kube-prometheus-stack) ─────────────────────────────────────── - name: prometheus namespace: monitoring createNamespace: true chart: prometheus-community/kube-prometheus-stack values: - k8s/monitoring/prometheus-values.yaml hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh monitoring - events: ["postsync"] command: bash args: - -c - | # Raw manifests that use the PrometheusRule/ServiceMonitor CRDs this # release just installed — must apply after the operator is up. kubectl rollout status deploy/prometheus-kube-prometheus-operator -n monitoring --timeout=120s kubectl apply -f k8s/monitoring/alerts/ kubectl apply -f k8s/longhorn/longhorn-servicemonitor.yaml - name: blackbox-exporter namespace: monitoring chart: prometheus-community/prometheus-blackbox-exporter version: "~11" values: - k8s/monitoring/blackbox-exporter-values.yaml needs: - monitoring/prometheus - ingress-nginx/ingress-nginx # ── CI/CD (Forgejo + runner + Argo CD) ─────────────────────────────────────── # Required env vars: # FORGEJO_ADMIN_PASSWORD — bootstrap admin password (set once, stored in .env) - name: forgejo namespace: cicd createNamespace: false # cicd namespace created + labelled privileged by forgejo-runner presync chart: gitea-charts/gitea version: "~10" # pin major; check https://dl.gitea.com/charts/ for current values: - k8s/talos-ci-cd/forgejo-values.yaml set: - name: gitea.admin.password value: {{ env "FORGEJO_ADMIN_PASSWORD" }} - name: gitea.config.oauth2.ENABLED value: "true" - name: gitea.config.oauth2.PROVIDER value: "openidconnect" - name: gitea.config.oauth2.OPENID_CONNECT_DISCOVERY_URL value: "https://authentik.{{ env "CLUSTER_DOMAIN" }}/application/o/forgejo/.well-known/openid-configuration" - name: gitea.config.oauth2.CLIENT_ID value: "forgejo" - name: gitea.config.oauth2.CLIENT_SECRET value: {{ env "FORGEJO_OIDC_CLIENT_SECRET" }} needs: - iam/authentik hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh cicd # CA trust: read from cert-manager source-of-truth (never hardcoded) CA_PEM=$(kubectl get secret homelab-ca-secret -n cert-manager \ -o jsonpath='{.data.tls\.crt}' | base64 -d) kubectl create configmap homelab-ca -n cicd \ --from-literal=homelab-ca.crt="$CA_PEM" \ --dry-run=client -o yaml | kubectl apply -f - kubectl apply -f k8s/ingress/ingress.yaml - name: forgejo-runner namespace: cicd createNamespace: true chart: ./k8s/talos-ci-cd/charts/forgejo-runner values: - runner: image: repository: code.forgejo.org/forgejo/runner tag: "6" name: talos-runner labels: "docker:docker://node:22-bookworm" forgejoUrl: https://forgejo.{{ env "CLUSTER_DOMAIN" }} tokenSecret: runner-token resources: requests: cpu: 100m memory: 256Mi limits: cpu: "2" memory: 4Gi dind: image: repository: docker tag: "27-dind" resources: requests: cpu: 100m memory: 256Mi limits: cpu: "2" memory: 4Gi persistence: reg: storageClass: longhorn size: 1Gi dind: storageClass: longhorn size: 30Gi tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule needs: - cicd/forgejo hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh cicd # CA secret so the runner can verify Forgejo's TLS cert kubectl -n cicd create secret generic homelab-ca \ --from-file=ca.crt=k8s/forge/pki/ca.crt \ --dry-run=client -o yaml | kubectl apply -f - # Wait for Forgejo to be ready, then fetch + store the runner token kubectl -n cicd rollout status deploy/forgejo --timeout=120s TOKEN=$(kubectl -n cicd exec deploy/forgejo -- \ forgejo actions generate-runner-token 2>/dev/null | tr -d '\r\n') kubectl -n cicd create secret generic runner-token \ --from-literal=token="$TOKEN" \ --dry-run=client -o yaml | kubectl apply -f - echo "Runner token stored in cicd/runner-token" - events: ["postsync"] command: kubectl args: ["apply", "-f", "k8s/forge/runner-gc-cronjob.yaml"] # ── CI/CD (Argo CD) ────────────────────────────────────────────────────────── # Forgejo + runner are bootstrapped via kubectl apply (forge/forgejo.yaml, forge/runner.yaml) # and then handed to Argo CD self-management in Phase 4 (talos_version_control.html). # Only Argo CD itself lives here — it can't manage its own initial install. # # Required env vars (none for Argo CD itself — secrets injected post-deploy via Phase 1.5.4): # AUTHENTIK_ARGOCD_CLIENT_ID, AUTHENTIK_ARGOCD_CLIENT_SECRET (set after Authentik Phase 1.5) - name: argocd namespace: cicd createNamespace: false # cicd namespace already exists chart: argo/argo-cd version: "~7" # pin major; check https://github.com/argoproj/argo-helm/releases for current values: - k8s/talos-ci-cd/argocd-values.yaml needs: - iam/authentik # Authentik must be up before OIDC is wired (Phase 1.5.4) hooks: - events: ["presync"] command: bash args: - -c - | # oidc-secret holds Authentik client credentials — referenced by argocd-cm via $oidc-secret:key # Must be labelled part-of: argocd so Argo CD's credential templating can read it kubectl -n cicd create secret generic oidc-secret \ --from-literal=client-id="{{ env "AUTHENTIK_ARGOCD_CLIENT_ID" }}" \ --from-literal=client-secret="{{ env "AUTHENTIK_ARGOCD_CLIENT_SECRET" }}" \ --dry-run=client -o yaml \ | kubectl label --local -f - app.kubernetes.io/part-of=argocd -o yaml \ | kubectl apply -f - - events: ["postsync"] command: bash args: - -c - | # Inject homelab-ca into argocd-tls-certs-cm so Argo CD can verify Authentik TLS CA_PEM=$(kubectl get configmap homelab-ca -n iam \ -o jsonpath='{.data.homelab-ca\.crt}') AUTHENTIK_HOST="authentik.{{ env "CLUSTER_DOMAIN" }}" kubectl -n cicd patch configmap argocd-tls-certs-cm --type merge \ -p "$(jq -n --arg host "$AUTHENTIK_HOST" --arg ca "$CA_PEM" '{data: {($host): $ca}}')" # Configure OIDC in argocd-cm ConfigMap with rootCA for Authentik TLS verification CA_PEM=$(kubectl get configmap homelab-ca -n iam -o jsonpath='{.data.homelab-ca\.crt}') AUTHENTIK_HOST="authentik.{{ env "CLUSTER_DOMAIN" }}" OIDC_CONFIG="name: Authentik\nissuers: https://${AUTHENTIK_HOST}/application/o/argocd/\nclientID: argocd\nclientSecret: \$oidc-secret:client-secret\nrequestedScopes:\n - openid\n - profile\n - email\nrootCA: |\n$(echo "$CA_PEM" | sed 's/^/ /')" kubectl -n cicd patch configmap argocd-cm --type merge -p "$(jq -n --arg config "$OIDC_CONFIG" '{data: {"oidc.config": $config}}')" # ── Dashboard (Portainer) ───────────────────────────────────────────────────── - name: portainer namespace: dashboard createNamespace: true chart: portainer/portainer values: - k8s/portainer/portainer-values.yaml hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh dashboard # ── Messaging Queue (SQS-like Kafka) ───────────────────────────────────────── - name: strimzi-operator namespace: sqs createNamespace: true chart: strimzi/strimzi-kafka-operator version: 0.46.0 values: - watchNamespaces: ["sqs"] - name: kafka-cluster namespace: sqs chart: k8s/sqs/charts/kafka-cluster needs: - sqs/strimzi-operator values: - namespace: sqs nodePool: replicas: 3 storage: class: longhorn-kafka sizeGi: 10 resources: memory: 5Gi cpu: "2" - name: kmsvc-redis namespace: sqs chart: bitnami/redis version: 20.6.0 values: - architecture: standalone global: security: allowInsecureImages: true image: repository: bitnamilegacy/redis auth: enabled: false master: persistence: enabled: true storageClass: longhorn size: 2Gi resources: limits: memory: 1Gi requests: memory: 1Gi - name: queue-crd namespace: sqs chart: k8s/sqs/charts/queue-crd needs: - sqs/kafka-cluster - sqs/kmsvc-redis values: - namespace: sqs kafkaBrokers: "{{ env "KAFKA_BOOTSTRAP" }}" redisAddr: "{{ env "REDIS_ADDR" }}" - name: management-service namespace: sqs chart: k8s/sqs/charts/management-service needs: - sqs/kafka-cluster - sqs/kmsvc-redis values: - namespace: sqs env: kafkaBrokers: "{{ env "KAFKA_BOOTSTRAP" }}" redisAddr: "{{ env "REDIS_ADDR" }}" authentikIssuerURL: "https://authentik.{{ env "CLUSTER_DOMAIN" }}/application/o/kafaka/" authentikAudience: "QI0gPtR99ar8VvhK8Tqox4SDkTKzbNU7lbgwBNSc" ingress: enabled: true host: kmsvc.{{ env "CLUSTER_DOMAIN" }} clusterIssuer: homelab-ca # ── Story Crater Backend ─────────────────────────────────────────────────────── # Story Crater Backend — migrated to CloudNativePG in ddb namespace # The story_crater database is created by CNPG bootstrap.initdb.postInitApplicationSQL. # Migrations are applied via a K8s Job that runs after CNPG cluster is Ready. - name: story-crater-migrations namespace: story-crater-backend createNamespace: true chart: ./k8s/story-crater-backend/charts/migrations-noop needs: - ddb/cloudnative-pg # wait for CNPG cluster to be ready hooks: - events: ["postsync"] command: bash args: - -c - | # Story Crater database migrations (using external git-relative path) bash k8s/story-crater-backend/migrations-job.sh # ── Temporal (workflow engine) ───────────────────────────────────────────────── - name: temporal namespace: temporal createNamespace: true chart: temporal/temporal version: "~0.55" values: - k8s/temporal/temporal-values.yaml needs: - monitoring/prometheus set: - name: web.auth.providers[0].clientId value: {{ env "AUTHENTIK_TEMPORAL_CLIENT_ID" }} - name: web.auth.providers[0].clientSecret value: {{ env "AUTHENTIK_TEMPORAL_CLIENT_SECRET" }} hooks: - events: ["presync"] command: bash args: - -c - | bash k8s/base/namespace-setup.sh temporal kubectl apply -f k8s/temporal/elasticsearch.yaml kubectl create secret generic temporal-oidc -n temporal \ --from-literal=client_id="{{ env "AUTHENTIK_TEMPORAL_CLIENT_ID" }}" \ --from-literal=client_secret="{{ env "AUTHENTIK_TEMPORAL_CLIENT_SECRET" }}" \ --dry-run=client -o yaml | kubectl apply -f - # ── OAuth2-Proxy (Authentik OIDC protection) ────────────────────────────────── - name: oauth2-proxy-temporal namespace: temporal chart: ./k8s/charts/oauth2-proxy values: - k8s/charts/oauth2-proxy/values-temporal.yaml needs: - ingress-nginx/ingress-nginx hooks: - events: ["presync"] command: bash args: - -c - | kubectl create secret generic temporal-oidc -n temporal \ --from-literal=clientSecret="{{ env "AUTHENTIK_TEMPORAL_CLIENT_SECRET" }}" \ --from-literal=cookieSecret="$(head -c 32 /dev/urandom | base64)" \ --dry-run=client -o yaml | kubectl apply -f - - name: oauth2-proxy-kmsvc namespace: sqs chart: ./k8s/charts/oauth2-proxy values: - k8s/charts/oauth2-proxy/values-kmsvc.yaml needs: - ingress-nginx/ingress-nginx hooks: - events: ["presync"] command: bash args: - -c - | kubectl create secret generic kmsvc-oidc -n sqs \ --from-literal=clientSecret="{{ env "AUTHENTIK_KMSVC_CLIENT_SECRET" }}" \ --from-literal=cookieSecret="$(head -c 32 /dev/urandom | base64)" \ --dry-run=client -o yaml | kubectl apply -f - - name: oauth2-proxy-longhorn namespace: longhorn-system chart: ./k8s/charts/oauth2-proxy values: - k8s/charts/oauth2-proxy/values-longhorn.yaml needs: - ingress-nginx/ingress-nginx hooks: - events: ["presync"] command: bash args: - -c - | kubectl create secret generic longhorn-oidc -n longhorn-system \ --from-literal=clientSecret="{{ env "AUTHENTIK_LONGHORN_CLIENT_SECRET" }}" \ --from-literal=cookieSecret="$(head -c 32 /dev/urandom | base64)" \ --dry-run=client -o yaml | kubectl apply -f - - name: oauth2-proxy-portainer namespace: dashboard chart: ./k8s/charts/oauth2-proxy values: - k8s/charts/oauth2-proxy/values-portainer.yaml needs: - ingress-nginx/ingress-nginx hooks: - events: ["presync"] command: bash args: - -c - | kubectl create secret generic portainer-oidc -n dashboard \ --from-literal=clientSecret="{{ env "AUTHENTIK_PORTAINER_CLIENT_SECRET" }}" \ --from-literal=cookieSecret="$(head -c 32 /dev/urandom | base64)" \ --dry-run=client -o yaml | kubectl apply -f - # ── Ollama LLM Server ──────────────────────────────────────────────────────── - name: ollama namespace: llm createNamespace: true chart: ./k8s/llm/charts/ollama values: - k8s/llm/charts/ollama/values.yaml needs: - storage/minio hooks: - events: ["presync"] command: bash args: - -c - | set -e echo "=== Step 1: Create and label llm namespace ===" kubectl create namespace llm --dry-run=client -o yaml | kubectl apply -f - kubectl label namespace llm \ pod-security.kubernetes.io/enforce=baseline \ pod-security.kubernetes.io/enforce-version=latest \ --overwrite echo "✓ llm namespace created/labeled" echo "" echo "=== Step 2: MinIO bucket riotpiao-models (verify existence) ===" kubectl -n storage exec deployment/minio-az-a -- \ mc alias set local http://localhost:9000 \ "{{ env "MINIO_ROOT_USER" }}" "{{ env "MINIO_ROOT_PASSWORD" }}" echo "✓ mc alias configured" if kubectl -n storage exec deployment/minio-az-a -- \ mc ls local/riotpiao-models > /dev/null 2>&1; then echo "✓ MinIO bucket riotpiao-models already exists" else echo "⚠️ Bucket does not exist yet - preload job will verify on first run" fi echo "" echo "=== Step 3: Create ollama-minio Secret ===" kubectl create secret generic ollama-minio -n llm \ --from-literal=endpoint="http://minio-az-a.storage:9000" \ --from-literal=bucket="riotpiao-models" \ --from-literal=access_key="{{ env "MINIO_ROOT_USER" }}" \ --from-literal=secret_key="{{ env "MINIO_ROOT_PASSWORD" }}" \ --dry-run=client -o yaml | kubectl apply -f - echo "✓ Secret ollama-minio created/updated" echo "" echo "=== Step 4: Create ollama-oidc Secret ===" kubectl create secret generic ollama-oidc -n llm \ --from-literal=client_id="{{ env "AUTHENTIK_OLLAMA_CLIENT_ID" }}" \ --from-literal=client_secret="{{ env "AUTHENTIK_OLLAMA_CLIENT_SECRET" }}" \ --dry-run=client -o yaml | kubectl apply -f - echo "✓ Secret ollama-oidc created/updated" # ── Claude Terminal (persistent dev environment) ───────────────────────────── - name: claude-terminal namespace: dev-tools createNamespace: true chart: ./k8s/dev-tools values: - k8s/dev-tools/values.yaml # ── Global Reloader Configuration ──────────────────────────────────────────── hooks: - events: ["postsync"] command: bash args: - -c - | # Enable Reloader on all Secrets and ConfigMaps to auto-restart pods on updates for ns in cert-manager ingress-nginx monitoring logging storage iam cicd temporal dashboard story-crater-backend llm dev-tools ddb; do echo "Patching $ns Secrets..." kubectl patch secret -n $ns --all -p '{"metadata":{"annotations":{"secrets.stakater.com/match":"true"}}}' 2>/dev/null || true echo "Patching $ns ConfigMaps..." kubectl patch configmap -n $ns --all -p '{"metadata":{"annotations":{"configmap.reloader.stakater.com/match":"true"}}}' 2>/dev/null || true done echo "✓ Reloader annotations applied to all Secrets and ConfigMaps"