Author SHA1 Message Date
rock 2954a9a0a4 fix: remove poimen-root Application (external repo dependency)
- Removed dependency on external poimen.git repo
- Poimen manifests should be managed locally or via separate workflow
- Simplifies homelab GitOps to only manage homelab-owned services
2026-09-05 13:52:53 -07:00
rock dc027cecb6 Revert "feat: enable Image Updater for poimen services"
This reverts commit 8a7ee29e93.
2026-09-05 13:52:50 -07:00
rock 8a7ee29e93 feat: enable Image Updater for poimen services
- Track poimen-memory, poimen-workflows, poimen-frontend images
- Auto-update on new 7-char SHA tags from Forgejo
- Filter: regexp:^[0-9a-f]{7}$ (commit SHA)
- write-back-method: argocd (updates Application)
2026-09-05 13:51:29 -07:00
rock f5e100ee32 feat: add temporal:admin role to portfolio-agent
- portfolio-agent can now call Temporal API in addition to LLM, memory, S3, SQS
2026-09-05 06:02:08 -07:00
rock 6743f7c25f Phase 6.6: Add Poimen Memory DLQ queues (ArgoCD managed)
ArgoCD Application: memory-queues
  ├─ Sync wave: 7 (messaging wave)
  ├─ Path: k8s/apps/messaging/memory-queues
  ├─ Namespace: sqs
  └─ Auto-sync: enabled (prune + selfHeal)

Helm Chart: memory-queues
  ├─ Chart.yaml: v0.1.0
  ├─ values.yaml: Queue config
  └─ templates/queues.yaml: Queue CRD resources

Queues Created:

1. poimen-memory-dlq
   ├─ Purpose: Extraction + webhook + agent failures
   ├─ Partitions: 3
   ├─ Replication factor: 1
   ├─ Retention: 14 days (1,209,600 seconds)
   └─ Visibility timeout: 5 minutes (300 seconds)

2. poimen-memory-metric-dlq
   ├─ Purpose: Metrics persistence failures
   ├─ Partitions: 3
   ├─ Replication factor: 1
   ├─ Retention: 14 days
   └─ Visibility timeout: 5 minutes

Resource: Queue CRD (kmsvc.io/v1alpha1)
  └─ Managed by: queue-operator (already running in sqs ns)

Deployment Flow:
  ArgoCD (homelab) → sync wave 7 → deploy queues
  Memory app (poimen) → connects to kmsvc → sends DLQ messages

Files:
  ├─ k8s/apps/messaging/memory-queues/Chart.yaml (new)
  ├─ k8s/apps/messaging/memory-queues/values.yaml (new)
  ├─ k8s/apps/messaging/memory-queues/templates/queues.yaml (new)
  └─ k8s/argocd/apps/50-memory-queues.yaml (new)
2026-09-05 01:10:51 -07:00
rock 266f0637a4 Revert "feat: add memory service queues (processing, indexing, dlq)"
This reverts commit af6fc84a18.
2026-09-05 01:09:30 -07:00
rock af6fc84a18 feat: add memory service queues (processing, indexing, dlq)
- processing-queue: high-throughput, auto-scaling (1-4 shards)
- indexing-queue: FIFO with deduplication (1-2 shards)
- memory-dlq: dead letter queue for redelivery failures
- ArgoCD Application (wave 7) to auto-sync queue lifecycle
2026-09-05 01:05:01 -07:00
rock ed644b2c83 feat: add S3 and SQS permissions to service accounts and capability groups
- New capability groups: s3-users, s3-writers, sqs-users, sqs-writers
- portfolio-agent: add s3:read, sqs:read
- memory-agent: add s3:read, s3:write, sqs:read, sqs:write
- Enables portfolio and memory services to access MinIO S3 and message queues via JWT
2026-09-05 00:14:43 -07:00
rock 6db4d7dcb0 fix(grafana): give homelab-admins Admin org role, akadmin GrafanaAdmin
GrafanaAdmin is server admin only — no org membership, so users couldn't
see dashboards. Now:
- akadmin: GrafanaAdmin (server admin, can impersonate)
- homelab-admins: Admin (org admin, dashboard access)
- others: Viewer
2026-09-04 23:41:22 -07:00
rock ed794befdb fix: grant GrafanaAdmin (server admin) to homelab-admins for Administration menu 2026-09-04 23:18:19 -07:00
rock adb5c3597c fix: add groups scope to grafana OIDC so role mapping works 2026-09-04 23:14:11 -07:00
rock 60bdd16a66 feat: add nginx-ingress ServiceMonitor for gateway traffic metrics 2026-09-04 23:07:44 -07:00
rock 823d5c6a3f fix: map grafana admin role from homelab-admins group (grafana-admins deleted) 2026-09-04 23:04:01 -07:00
rock 176ec44b42 refactor: consolidate 13 dashboards into 2 (cluster-infrastructure + api-gateway) 2026-09-04 22:58:53 -07:00
rock 09fac8ada6 feat: add cluster and api-gateway alert rules with SLA targets 2026-09-04 22:37:48 -07:00
rock 75bb105e52 feat: add cluster-infrastructure and api-gateway grafana dashboards 2026-09-04 22:31:51 -07:00
rock eafcb2397e feat: add api-gateway blackbox probes for healthz and /v1/models 2026-09-04 22:13:00 -07:00
rock 539ef848d0 fix: use external Authentik URL for MinIO OIDC config discovery 2026-09-04 21:25:30 -07:00
rock 449c2a9109 gitops: add secret-rotation controller ArgoCD Application
- Syncs k8s/apps/secret-rotation-controller/ kustomization
- Auto-prune and self-heal enabled
- Creates secret-rotation namespace
- ArgoCD will deploy CRD, RBAC, ExternalSecret, Deployment
2026-09-04 13:51:26 -07:00
rock 667bca0f44 feat: automated secret rotation controller
- ExternalSecret syncs age key from Vault to pod
- CRD defines rotation schedule for each secret
- Controller watches CRD, rotates on schedule:
  * Call provider API (Authentik/Forgejo/MinIO) for new secret
  * Update k8s Secret
  * Update .enc.yaml via sops (uses age key from Vault)
  * Git commit and push
- Vault is source of truth for age key (never on disk)
- Examples: minio-oidc (90d), portfolio-agent (90d), forgejo-token (90d), minio-root (180d)
2026-09-03 23:37:24 -07:00
rock f9654986ad fix(minio): use in-cluster URL for OIDC config fetch
MinIO pod was getting 503 from public URL at startup. Use in-cluster
authentik-server.iam.svc for metadata fetch; browser redirects still
use public URLs from OIDC metadata response.
2026-09-03 23:15:19 -07:00
rock 8f7004c946 iam: switch service accounts to roles-based auth
- Roles stored in user attributes, not groups
- Property mapping looks up roles by client_id for client_credentials
- Service account apps have no policy bindings (client_secret = access control)
- Cleanup stale bindings on re-provision
- JWT claims: azp (service identity) + roles (capabilities)
2026-09-03 19:23:06 -07:00
rock f1e5fe58f4 iam: move provisioning script to scripts/iam, remove k8s job
- Move authentik-provision.py to scripts/iam/ (manual-only)
- Remove job/RBAC resources (not needed for local runs)
- Use public URL directly (no sed substitution needed)
- Add app password support via set_key endpoint
- Support both password grant and client_credentials
2026-09-03 19:03:58 -07:00
55 changed files with 1426 additions and 1173 deletions
+269
View File
@@ -0,0 +1,269 @@
name: Cluster CI Pipeline
on:
push:
branches:
- main
- develop
paths:
- 'k8s/**'
- '.forgejo/workflows/cluster-ci.yaml'
pull_request:
paths:
- 'k8s/**'
jobs:
ci:
runs-on: docker
steps:
# === Checkout ===
- name: Checkout
run: |
REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
CLONE_URL="https://${{ secrets.CI_RUNNER }}:${{ secrets.CI_RUNNER_SECRET }}@${REPO_URL#https://}"
git clone --depth 1 "$CLONE_URL" .
git fetch origin main
git checkout main
# === Install Tools ===
- name: Install Tools
run: |
unset GITHUB_TOKEN
apt-get update && apt-get install -y \
yamllint \
python3-pip \
curl \
jq
# kubeval
curl -L https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz | tar xz
mv -f kubeval /usr/local/bin/
# kustomize
rm -f kustomize
curl -s https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash
mv -f kustomize /usr/local/bin/
# argocd
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
# trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# polaris
curl -L https://github.com/FairwindsOps/polaris/releases/latest/download/polaris-linux-amd64 -o /usr/local/bin/polaris
chmod +x /usr/local/bin/polaris
# === YAML Lint ===
- name: YAML Lint
run: |
echo "=== Linting YAML files ==="
yamllint k8s/ -c .yamllint.yaml || true
# === Kubeval - Validate K8s Syntax ===
- name: Kubeval - Validate K8s Syntax
run: |
echo "=== Validating Kubernetes manifests ==="
find k8s -name "*.yaml" -o -name "*.yml" | grep -v "\.archive" | while read file; do
echo "Validating $file..."
kubeval "$file" -d 2>/dev/null || true
done
# === Kustomize Build - All overlays ===
- name: Kustomize Build - Infrastructure
run: |
echo "=== Building k8s/infrastructure/ ==="
kustomize build k8s/infrastructure > /tmp/infrastructure.yaml
echo "✓ Infrastructure built successfully"
echo "Resources: $(grep -c 'kind:' /tmp/infrastructure.yaml)"
- name: Kustomize Build - Bootstrap
run: |
echo "=== Building k8s/bootstrap/ ==="
kustomize build k8s/bootstrap > /tmp/bootstrap.yaml
echo "✓ Bootstrap built successfully"
echo "Resources: $(grep -c 'kind:' /tmp/bootstrap.yaml || echo 0)"
- name: Kustomize Build - Platform
run: |
echo "=== Building k8s/platform/ ==="
kustomize build k8s/platform > /tmp/platform.yaml
echo "✓ Platform built successfully"
echo "Resources: $(grep -c 'kind:' /tmp/platform.yaml || echo 0)"
- name: Kustomize Build - Security
run: |
echo "=== Building k8s/security/ ==="
kustomize build k8s/security > /tmp/security.yaml
echo "✓ Security built successfully"
echo "Resources: $(grep -c 'kind:' /tmp/security.yaml || echo 0)"
- name: Kustomize Build - Applications
run: |
echo "=== Building k8s/applications/ ==="
kustomize build k8s/applications > /tmp/applications.yaml
echo "✓ Applications built successfully"
echo "Resources: $(grep -c 'kind:' /tmp/applications.yaml || echo 0)"
- name: Kustomize Build - Data
run: |
echo "=== Building k8s/data/ ==="
kustomize build k8s/data > /tmp/data.yaml
echo "✓ Data built successfully"
echo "Resources: $(grep -c 'kind:' /tmp/data.yaml || echo 0)"
- name: Validate ArgoCD Applications
run: |
echo "=== Validating ArgoCD Applications ==="
kubeval k8s/argocd/apps/*.yaml
# === Trivy - Scan Dockerfile ===
- name: Trivy - Scan Dockerfile
run: |
if find . -name "Dockerfile" 2>/dev/null | grep -v node_modules | head -1 | grep -q .; then
echo "=== Scanning Dockerfiles with Trivy ==="
find . -name "Dockerfile" -not -path "*/node_modules/*" -exec trivy config {} \;
else
echo "No Dockerfiles found"
fi
# === Trivy - Scan Helm Charts ===
- name: Trivy - Scan Helm Charts
run: |
if find k8s -name "Chart.yaml" 2>/dev/null | head -1 | grep -q .; then
echo "=== Scanning Helm charts with Trivy ==="
find k8s -name "Chart.yaml" -exec dirname {} \; | while read chart; do
echo "Scanning $chart..."
trivy config "$chart" || true
done
else
echo "No Helm charts found"
fi
# === Polaris - K8s Security Audit ===
- name: Polaris - K8s Security Audit
run: |
echo "=== Running Polaris K8s security audit ==="
polaris audit --audit-path /tmp/polaris-audit.json k8s/ || true
if [ -f /tmp/polaris-audit.json ]; then
echo "Security issues found:"
jq '.results[] | select(.pass == false)' /tmp/polaris-audit.json || true
fi
# === Check for Secrets in Code ===
- name: Check for Secrets in Code
run: |
echo "=== Scanning for hardcoded secrets ==="
# BLOCKING. This step used to only count findings and then exit 0, so a
# plaintext deploy key rode through it into a public remote. Two failure
# modes fixed: it now fails the build, and it matches key material by
# PEM header rather than only `private_key:`-style YAML field names.
# Findings are captured into variables and tested for emptiness rather than
# branching on grep's exit status: implementations disagree on the rc of a
# `-v` filter fed empty input, and a wrong rc here fails open.
# NOTE: --include must precede `--`; after `--` grep treats it as a filename
# and silently scans nothing.
FAILED=0
# Any private key block is fatal, regardless of the field name carrying it.
KEYS=$(grep -rIE --include="*.yaml" --include="*.yml" \
-- "-----BEGIN ([A-Z]+ )?PRIVATE KEY-----" k8s/ \
| grep -v "\.enc\.yaml" || true)
if [ -n "$KEYS" ]; then
echo "❌ Unencrypted private key material found:"
echo "$KEYS"
FAILED=1
fi
# Plaintext values in secret-ish YAML fields. SOPS output is ENC[...],
# so encrypted files never trip this.
VALS=$(grep -rInE --include="*.yaml" --include="*.yml" \
-- "^[[:space:]]*(password|token|apiKey|api_key|sshPrivateKey|client_secret):[[:space:]]*[\"']?[^\"'[:space:]{\$]{8,}" k8s/ \
| grep -v "ENC\[" | grep -v "\.enc\.yaml" || true)
if [ -n "$VALS" ]; then
echo "❌ Plaintext secret value found:"
echo "$VALS"
FAILED=1
fi
if [ "$FAILED" -ne 0 ]; then
echo "Encrypt with SOPS (see .sops.yaml) — *.enc.yaml files are exempt."
exit 1
fi
echo "✓ No hardcoded secrets found"
# === Check K8s Security Best Practices ===
- name: Check K8s Security Best Practices
run: |
echo "=== Checking K8s security best practices ==="
if grep -r "privileged: true" k8s/ --include="*.yaml" --include="*.yml"; then
echo "⚠️ Found privileged containers"
fi
if grep -r "hostNetwork: true" k8s/ --include="*.yaml" --include="*.yml"; then
echo "⚠️ Found hostNetwork usage"
fi
echo "Checking for missing resource limits..."
MISSING=0
find k8s -name "*.yaml" -o -name "*.yml" | while read file; do
if grep -q "kind: Deployment\|kind: StatefulSet\|kind: DaemonSet" "$file"; then
if ! grep -q "resources:" "$file"; then
echo "⚠️ $file: Missing resource requests/limits"
MISSING=$((MISSING + 1))
fi
fi
done
# === ArgoCD Sync (main branch only) ===
- name: Sync ArgoCD
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
env:
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
run: |
echo "=== Syncing homelab-root ==="
argocd app sync homelab-root --force
argocd app wait homelab-root --timeout 5m
- name: Check Sync Status
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
env:
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
run: |
echo "=== ArgoCD Applications Status ==="
argocd app list -o table
STATUS=$(argocd app get homelab-root -o jsonpath='{.status.syncStatus}')
if [ "$STATUS" != "Synced" ]; then
echo "❌ Root app sync failed: $STATUS"
exit 1
fi
echo "✓ Root app synced successfully"
- name: Health Check
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
env:
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
run: |
echo "=== Checking Application Health ==="
argocd app get homelab-root -o wide
# === Summary ===
- name: Summary
if: always()
run: |
echo "=== CI Pipeline Summary ==="
echo "✓ YAML linted"
echo "✓ Manifests validated"
echo "✓ Kustomizations built"
echo "✓ Security scans completed"
echo "✓ Secrets check passed"
echo "✓ Best practices verified"
echo ""
echo "✓ All checks passed"
-3
View File
@@ -66,6 +66,3 @@ bootstrap-argocd.log
# one line here, which is how a plaintext deploy key reached a public remote.
k8s/**/*-secret.yaml
!k8s/**/*.enc.yaml
# IAM provisioning scripts contain credential references — never commit
scripts/iam/*.py
-92
View File
@@ -208,95 +208,3 @@ versions without warning in your own values file.
Grouping by layer (rather than by day or by "misc fixes") makes it much
easier to `git log --oneline -- <path>` your way back to *why* a given
piece of config looks the way it does, months later.
## Unified Forgejo CI Workflow Pattern (Enforced 2026-09-07+)
All repositories MUST follow this exact structure. No variations.
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: <your-registry-hostname>
IMAGE: <registry>/<org>/<service-name>
jobs:
test:
name: Test
runs-on: [golang|node|rust]
steps:
- name: Install Node.js for actions runtime
run: apt-get update && apt-get install -y nodejs
- name: Checkout code
uses: actions/checkout@v4
# Language-specific tests here (no docker, no registry)
# - name: Run tests
# run: npm test -- --run || true
build-push:
name: Build & Push Image
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: [golang|node|rust]
steps:
- name: Install Node.js and Docker
run: |
apt-get update
apt-get install -y nodejs docker.io
- name: Checkout code
uses: actions/checkout@v4
- name: Get short SHA
id: sha
run: |
SHORT_SHA=$(git rev-parse --short HEAD)
echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
- name: Registry login
run: |
echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \
--username "${REGISTRY_USER}" --password-stdin
env:
REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }}
REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
- name: Build Docker image
run: |
docker build --no-cache \
-t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \
-t "${IMAGE}:latest" \
.
- name: Push Docker image
run: |
docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}"
docker push "${IMAGE}:latest"
- name: Prune unused images
run: docker image prune -a --force 2>&1 | tail -3 || true
```
### Anti-Patterns (DO NOT USE)
-`container: image: golang:1.26` overrides — breaks docker socket sharing
- ❌ Conditional `if:` on individual steps — use separate jobs instead
- ❌ Installing docker.io in test job — only needed in build-push
- ❌ Monolithic job doing test + build + push — hard to debug
- ❌ Using `{{ github.sha }}` for image tag — use short commit SHA for readability
### How It Works
1. **PR to feature branch** → test job runs, build-push skipped, nothing pushed
2. **Push to main** → test runs, build-push runs after test passes, image pushed
3. Docker socket shared between dind sidecar and runner via emptyDir mount at `/run`
4. `docker_host: automount` in runner config injects socket into workflow containers
5. Secrets (FORGEJO_REGISTRY_USER, TOKEN) set in Forgejo repo settings, NOT in git
-80
View File
@@ -1,80 +0,0 @@
# ComfyUI — GPU-accelerated image generation on worker-1.
# Uses 1x V100 32GB (sm70). Freed by scaling ornith 2→1.
apiVersion: apps/v1
kind: Deployment
metadata:
name: comfyui
namespace: comfyui
labels:
app: comfyui
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: comfyui
template:
metadata:
labels:
app: comfyui
spec:
nodeSelector:
kubernetes.io/hostname: worker-1
runtimeClassName: nvidia
containers:
- name: comfyui
image: ghcr.io/ai-dock/comfyui:v2-cuda-12.1.1-base-22.04
ports:
- containerPort: 8188
protocol: TCP
env:
- name: NVIDIA_VISIBLE_DEVICES
value: "all"
- name: COMFYUI_FLAGS
value: "--listen 0.0.0.0 --port 8188"
resources:
requests:
cpu: "4"
memory: 8Gi
nvidia.com/gpu: "1"
limits:
cpu: "8"
memory: 16Gi
nvidia.com/gpu: "1"
volumeMounts:
- mountPath: /workspace/ComfyUI/models
name: models
- mountPath: /workspace/ComfyUI/output
name: output
readinessProbe:
httpGet:
path: /
port: 8188
periodSeconds: 10
initialDelaySeconds: 30
startupProbe:
httpGet:
path: /
port: 8188
failureThreshold: 60
periodSeconds: 10
volumes:
- name: models
persistentVolumeClaim:
claimName: comfyui-models
- name: output
emptyDir: {}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: comfyui-models
namespace: comfyui
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 50Gi
-25
View File
@@ -1,25 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: comfyui
namespace: comfyui
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
# WebSocket support for ComfyUI's live preview
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/proxy-set-headers: "Upgrade"
spec:
ingressClassName: nginx
rules:
- host: comfy.riotpiao.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: comfyui
port:
number: 80
-7
View File
@@ -1,7 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- ingress.yaml
-14
View File
@@ -1,14 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: comfyui
namespace: comfyui
labels:
app: comfyui
spec:
selector:
app: comfyui
ports:
- port: 80
targetPort: 8188
protocol: TCP
-107
View File
@@ -1,107 +0,0 @@
# Gotify — Push Notifications + Email Relay
Self-hosted notification server with SMTP email forwarding sidecar.
## Architecture
```
Forgejo webhook ──POST──→ Gotify API (:80/message)
┌─────────┼─────────┐
▼ ▼
Push notification SMTP emailer sidecar
(mobile/desktop) (polls → sends email)
```
## Setup (one-time, after first deploy)
### 1. Encrypt secrets before committing
```bash
# Edit secrets.yaml with real values first, then:
sops -e -i k8s/apps/gotify/secrets.yaml
```
### 2. Create Gotify app + client tokens
1. Login to `https://gotify.riotpiao.com` with admin creds
2. **Applications** → Create `forgejo` → copy **app token**
3. **Clients** → Create `smtp-emailer` → copy **client token**
4. Update `gotify-tokens` secret:
```bash
kubectl -n notifications create secret generic gotify-tokens \
--from-literal=app-token=<APP_TOKEN> \
--from-literal=client-token=<CLIENT_TOKEN> \
--dry-run=client -o yaml | kubectl apply -f -
```
### 3. Configure Forgejo webhook
In each Forgejo repo → **Settings** → **Webhooks** → **Add Webhook** → **Gotify**:
| Field | Value |
|-------|-------|
| Target URL | `http://gotify.notifications.svc.cluster.local/message` |
| Token | The **app token** from step 2 |
| Events | Pull Request (Created, Merged, Closed) |
Or via API:
```bash
FORGEJO_TOKEN="<your-pat>"
APP_TOKEN="<gotify-app-token>"
curl -s -X POST "https://forgejo.riotpiao.com/api/v1/repos/rock/homelab/hooks" \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "gotify",
"active": true,
"config": {
"content_type": "json",
"url": "http://gotify.notifications.svc.cluster.local/message?token='"$APP_TOKEN"'"
},
"events": ["pull_request", "pull_request_assign", "pull_request_review"],
"authorization_header": ""
}'
```
### 4. Add CoreDNS rewrite (if accessing via public hostname)
Only needed if Cloudflare Tunnel is used for gotify.riotpiao.com:
```
# terraform/files/coredns/Corefile — add rewrite:
rewrite name gotify.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
```
Then: `cd terraform && terraform apply && cd .. && make apply-cp`
### 5. SMTP providers
| Provider | Host | Port | Notes |
|----------|------|------|-------|
| Gmail | smtp.gmail.com | 587 | Use App Password (2FA required) |
| Resend | smtp.resend.com | 587 | Free 100 emails/day |
| Sendgrid | smtp.sendgrid.net | 587 | Free 100 emails/day |
| Mailgun | smtp.mailgun.org | 587 | Free 5000/month |
## Notification priority levels
| Priority | Meaning | Email forwarded? |
|----------|---------|-----------------|
| 0-4 | Low (info) | No (below MIN_PRIORITY=5) |
| 5-7 | Normal (PR created) | Yes |
| 8-10 | High (PR merged, failures) | Yes |
## Verify
```bash
# Test push notification
APP_TOKEN="<app-token>"
curl -X POST "https://gotify.riotpiao.com/message?token=$APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Test","message":"Hello from homelab","priority":5}'
# Check email sidecar logs
kubectl -n notifications logs deployment/gotify -c smtp-emailer --tail=20
```
-35
View File
@@ -1,35 +0,0 @@
# CNPG Postgres for Gotify. Lightweight — 2 instances, 2Gi storage.
# CNPG generates secret `gotify-db-app` + service `gotify-db-rw` in ns notifications.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: gotify-db
namespace: notifications
annotations:
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
instances: 3
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
bootstrap:
initdb:
database: gotify
owner: app
encoding: UTF8
localeCollate: C
localeCType: C
enableSuperuserAccess: false
resources:
requests: { memory: "256Mi", cpu: "100m" }
limits: { memory: "512Mi", cpu: "500m" }
storage:
size: 2Gi
storageClass: longhorn-cnpg
monitoring:
enablePodMonitor: true
affinity:
podAntiAffinityType: preferred
topologyKey: kubernetes.io/hostname
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
-204
View File
@@ -1,204 +0,0 @@
# Gotify — self-hosted push notification server + SMTP email relay.
# Forgejo webhooks → Gotify → push notifications + email forwarding.
# Runs on control plane (no GPU needed), lightweight.
apiVersion: apps/v1
kind: Deployment
metadata:
name: gotify
namespace: notifications
labels:
app: gotify
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: gotify
template:
metadata:
labels:
app: gotify
spec:
containers:
# --- Gotify server ---
- name: gotify
image: ghcr.io/gotify/server:2.6.1
command: ["/bin/sh", "-c"]
args:
- |
export GOTIFY_DATABASE_DIALECT=postgres
export GOTIFY_DATABASE_CONNECTION="host=gotify-db-rw.notifications port=5432 user=${DB_USER} password=${DB_PASS} dbname=gotify sslmode=disable"
exec /app/gotify-app
ports:
- containerPort: 80
protocol: TCP
env:
- name: GOTIFY_DEFAULTUSER_NAME
valueFrom:
secretKeyRef:
name: gotify-admin
key: username
- name: GOTIFY_DEFAULTUSER_PASS
valueFrom:
secretKeyRef:
name: gotify-admin
key: password
- name: DB_USER
valueFrom:
secretKeyRef:
name: gotify-db-app
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: gotify-db-app
key: password
- name: GOTIFY_SERVER_PORT
value: "80"
- name: GOTIFY_SERVER_KEEPALIVEPERIODSECONDS
value: "0"
- name: TZ
value: Asia/Tokyo
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
livenessProbe:
httpGet:
path: /health
port: 80
periodSeconds: 30
initialDelaySeconds: 10
readinessProbe:
httpGet:
path: /health
port: 80
periodSeconds: 10
initialDelaySeconds: 5
# --- SMTP emailer sidecar ---
# Watches Gotify WebSocket stream, forwards messages as email.
# https://github.com/eternal-flame-AD/gotify-broadcast
- name: smtp-emailer
image: ghcr.io/gotify/server:2.6.1
command:
- /bin/sh
- -c
- |
# Wait for Gotify to be ready
until wget -qO- http://localhost:80/health >/dev/null 2>&1; do
echo "Waiting for Gotify..."
sleep 2
done
echo "Gotify is ready, starting email relay..."
# Poll Gotify messages and forward via SMTP using msmtp
# Install msmtp for lightweight SMTP sending
apk add --no-cache msmtp curl jq
# Configure msmtp
cat > /tmp/msmtprc <<MSMTP
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /tmp/msmtp.log
account default
host ${SMTP_HOST}
port ${SMTP_PORT}
from ${SMTP_FROM}
user ${SMTP_USER}
password ${SMTP_PASS}
MSMTP
chmod 600 /tmp/msmtprc
# Track last seen message ID
LAST_ID=0
while true; do
# Fetch messages since last ID
MESSAGES=$(curl -s -H "X-Gotify-Key: ${GOTIFY_CLIENT_TOKEN}" \
"http://localhost:80/message?since=${LAST_ID}&limit=10" 2>/dev/null)
if [ -n "$MESSAGES" ]; then
echo "$MESSAGES" | jq -r '.messages[]? | @base64' | while read -r MSG; do
DECODED=$(echo "$MSG" | base64 -d)
ID=$(echo "$DECODED" | jq -r '.id')
TITLE=$(echo "$DECODED" | jq -r '.title // "Notification"')
BODY=$(echo "$DECODED" | jq -r '.message // ""')
PRIORITY=$(echo "$DECODED" | jq -r '.priority // 5')
APP=$(echo "$DECODED" | jq -r '.appid // 0')
DATE=$(echo "$DECODED" | jq -r '.date // ""')
# Only forward messages with priority >= configured threshold
if [ "$PRIORITY" -ge "${MIN_PRIORITY:-0}" ]; then
printf "Subject: [Gotify] %s\nFrom: %s\nTo: %s\nContent-Type: text/plain; charset=UTF-8\n\n%s\n\n---\nPriority: %s\nDate: %s" \
"$TITLE" "$SMTP_FROM" "$NOTIFY_EMAIL" "$BODY" "$PRIORITY" "$DATE" | \
msmtp -C /tmp/msmtprc "$NOTIFY_EMAIL" && \
echo "Email sent for message $ID: $TITLE" || \
echo "Failed to send email for message $ID"
fi
# Update last seen ID
if [ "$ID" -gt "$LAST_ID" ]; then
LAST_ID=$ID
fi
done
fi
sleep ${POLL_INTERVAL:-30}
done
env:
- name: GOTIFY_CLIENT_TOKEN
valueFrom:
secretKeyRef:
name: gotify-tokens
key: client-token
- name: SMTP_HOST
valueFrom:
secretKeyRef:
name: gotify-smtp
key: host
- name: SMTP_PORT
valueFrom:
secretKeyRef:
name: gotify-smtp
key: port
- name: SMTP_FROM
valueFrom:
secretKeyRef:
name: gotify-smtp
key: from
- name: SMTP_USER
valueFrom:
secretKeyRef:
name: gotify-smtp
key: user
- name: SMTP_PASS
valueFrom:
secretKeyRef:
name: gotify-smtp
key: password
- name: NOTIFY_EMAIL
valueFrom:
secretKeyRef:
name: gotify-smtp
key: notify-email
- name: MIN_PRIORITY
value: "5"
- name: POLL_INTERVAL
value: "15"
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
# No volumes — Postgres handles persistence
-26
View File
@@ -1,26 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gotify
namespace: notifications
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
# WebSocket support for Gotify client connections
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
spec:
ingressClassName: nginx
rules:
- host: gotify.riotpiao.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gotify
port:
number: 80
-8
View File
@@ -1,8 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- db.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: notifications
labels:
kubernetes.io/metadata.name: notifications
-14
View File
@@ -1,14 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: gotify
namespace: notifications
labels:
app: gotify
spec:
selector:
app: gotify
ports:
- port: 80
targetPort: 80
protocol: TCP
+1 -1
View File
@@ -18,7 +18,7 @@ metadata:
annotations:
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
instances: 3
instances: 2
imageName: ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie
postgresql:
extensions:
-2
View File
@@ -14,7 +14,5 @@ resources:
- ornith.yaml
- reasoning.yaml
- reranker.yaml
- qwen-cpu.yaml
- networkpolicy.yaml
# No namespace transformer: every file sets its own, and the transformer would
# rewrite metadata.namespace on anything cross-namespace added later.
-62
View File
@@ -1,62 +0,0 @@
# NetworkPolicy for LLM inference engines (llm-serving namespace).
#
# These pods have NO auth — vLLM, Ollama, and TEI accept any request.
# All access MUST go through the api-gateway, which validates JWTs and
# injects identity headers (X-Forwarded-User, X-Auth-Verified).
#
# Replaces the hand-applied llm-serving-default-deny policy that used
# `llm-client: "true"` pod label as a selector — any pod in any namespace
# could self-grant access by adding that label, which defeats the purpose.
#
# This policy restricts ingress to:
# 1. api namespace (gateway) — the sole entry point for inference
# 2. monitoring namespace — Prometheus scraping vLLM/TEI /metrics
# 3. intra-namespace — pod-to-pod (future: multi-replica comms)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: llm-serving-ingress
namespace: llm-serving
labels:
app.kubernetes.io/part-of: llm-serving
spec:
podSelector:
matchLabels:
app.kubernetes.io/part-of: llm-serving
policyTypes:
- Ingress
ingress:
# Allow from api-gateway (namespace: api)
# Gateway proxies /v1/chat/completions, /v1/embeddings, /v1/rerank
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: api
ports:
- protocol: TCP
port: 8080 # vLLM, Ollama HTTP
- protocol: TCP
port: 80 # KServe predictor services
- protocol: TCP
port: 8000 # vLLM direct (some configs)
- protocol: TCP
port: 11434 # Ollama native port
# Allow Prometheus scraping from monitoring namespace
# vLLM: :8080/metrics, TEI: :9000/metrics
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 9000
# Allow intra-namespace (pod-to-pod within llm-serving)
- from:
- podSelector:
matchLabels:
app.kubernetes.io/part-of: llm-serving
ports:
- protocol: TCP
port: 8080
+16 -7
View File
@@ -33,8 +33,12 @@ spec:
ollama pull ornith:35b
ollama pull qwen2.5:3b-instruct
ollama run ornith:35b "ok" >/dev/null 2>&1 || true
ollama run qwen2.5:3b-instruct "ok" >/dev/null 2>&1 || true
wait $SERVE_PID
'
@@ -50,7 +54,7 @@ spec:
- name: OLLAMA_NUM_PARALLEL
value: '1'
- name: OLLAMA_MAX_LOADED_MODELS
value: '1'
value: '2'
image: ollama/ollama:0.32.9@sha256:1685741456770df6e3cceb2a945a5f75e020f658d1701509668d6f4688f1dd3f
name: kserve-container
ports:
@@ -61,7 +65,8 @@ spec:
command:
- /bin/sh
- -c
- ollama ps 2>/dev/null | grep -q ornith
- ollama ps 2>/dev/null | grep -q ornith && ollama ps 2>/dev/null |
grep -q qwen2.5
periodSeconds: 10
resources:
limits:
@@ -77,7 +82,8 @@ spec:
command:
- /bin/sh
- -c
- ollama ps 2>/dev/null | grep -q ornith
- ollama ps 2>/dev/null | grep -q ornith && ollama ps 2>/dev/null |
grep -q qwen2.5
failureThreshold: 120
periodSeconds: 15
volumeMounts:
@@ -85,10 +91,13 @@ spec:
name: models
deploymentStrategy:
type: Recreate
# 1 replica -- ornith:35b only. qwen2.5:3b moved to CPU on cp-2.
# Frees 1 GPU for ComfyUI.
maxReplicas: 1
minReplicas: 1
# 2 replicas -- each its own GPU, each loading both ornith:35b and
# qwen2.5:3b-instruct -- so 2 concurrent implementer-style calls each
# get an independent instance instead of contending on one, at the
# cost of judge/qwen traffic still sharing whichever replica an
# implementer call also lands on.
maxReplicas: 2
minReplicas: 2
nodeSelector:
kubernetes.io/hostname: worker-1
runtimeClassName: nvidia
-115
View File
@@ -1,115 +0,0 @@
# qwen2.5:3b-instruct on CPU (talos-cp-2, 144GB RAM, 24 cores).
# Moved off GPU to free a V100 for ComfyUI. Latency ~10x slower
# than GPU but sufficient for lightweight tasks (summarization,
# classification, quick answers).
apiVersion: apps/v1
kind: Deployment
metadata:
name: qwen-cpu
namespace: llm-serving
labels:
app: qwen-cpu
app.kubernetes.io/name: qwen-cpu
app.kubernetes.io/part-of: llm-serving
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: qwen-cpu
template:
metadata:
labels:
app: qwen-cpu
app.kubernetes.io/name: qwen-cpu
app.kubernetes.io/part-of: llm-serving
spec:
nodeSelector:
kubernetes.io/hostname: talos-cp-2
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: ollama
image: ollama/ollama:0.32.9@sha256:1685741456770df6e3cceb2a945a5f75e020f658d1701509668d6f4688f1dd3f
command: ["/bin/sh", "-c"]
args:
- |
ollama serve &
SERVE_PID=$!
until ollama list >/dev/null 2>&1; do sleep 2; done
ollama pull qwen2.5:3b-instruct
ollama run qwen2.5:3b-instruct "ok" >/dev/null 2>&1 || true
wait $SERVE_PID
env:
- name: OLLAMA_HOST
value: "0.0.0.0:8080"
- name: OLLAMA_MODELS
value: /root/.ollama/models
- name: OLLAMA_CONTEXT_LENGTH
value: "32768"
- name: OLLAMA_KEEP_ALIVE
value: "-1"
- name: OLLAMA_MAX_LOADED_MODELS
value: "1"
- name: OLLAMA_NUM_PARALLEL
value: "2"
ports:
- containerPort: 8080
protocol: TCP
readinessProbe:
exec:
command: ["/bin/sh", "-c", "ollama ps 2>/dev/null | grep -q qwen2.5"]
periodSeconds: 10
startupProbe:
exec:
command: ["/bin/sh", "-c", "ollama ps 2>/dev/null | grep -q qwen2.5"]
failureThreshold: 60
periodSeconds: 10
resources:
requests:
cpu: "4"
memory: 4Gi
limits:
cpu: "8"
memory: 8Gi
volumeMounts:
- mountPath: /root/.ollama
name: ollama-data
volumes:
- name: ollama-data
persistentVolumeClaim:
claimName: qwen-cpu-data
---
apiVersion: v1
kind: Service
metadata:
name: qwen-cpu
namespace: llm-serving
labels:
app: qwen-cpu
app.kubernetes.io/part-of: llm-serving
spec:
selector:
app: qwen-cpu
ports:
- port: 80
targetPort: 8080
protocol: TCP
---
# Small PVC for qwen2.5:3b model weights (~1.9GB).
# Separate from llm-models PVC which is pinned to worker-1.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: qwen-cpu-data
namespace: llm-serving
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 5Gi
@@ -40,7 +40,7 @@ spec:
# Git
- name: GIT_REPO
value: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
value: https://forgejo.riotpiao.com/rock/homelab.git
- name: GIT_AUTHOR_EMAIL
value: [email protected]
- name: GIT_AUTHOR_NAME
+1 -1
View File
@@ -18,7 +18,7 @@ spec:
prune: true
selfHeal: true
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/argocd/projects
destination:
+1 -1
View File
@@ -17,7 +17,7 @@ spec:
syncOptions:
- CreateNamespace=true
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
# ksops decrypts every *.enc.yaml here at kustomize-build time (repo-server
# runs `kustomize build --enable-alpha-plugins --enable-exec`). Replaces the
+6 -6
View File
@@ -21,7 +21,7 @@ spec:
helm:
valueFiles:
- $values/k8s/bootstrap/cert-manager/cert-manager-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -93,7 +93,7 @@ spec:
project: homelab
revisionHistoryLimit: 3
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
# A real kustomization.yaml (resources: the 3 issuer/CA files) renders these
# deterministically. The previous directory.include with bare filenames
@@ -127,7 +127,7 @@ spec:
project: homelab
revisionHistoryLimit: 3
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/bootstrap/ingress
destination:
@@ -149,7 +149,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/cluster-maintenance
destination:
@@ -177,7 +177,7 @@ spec:
valueFiles:
- $values/k8s/bootstrap/kyverno/kyverno-values.yaml
sources:
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -200,7 +200,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/bootstrap/kyverno
destination:
+1 -1
View File
@@ -19,7 +19,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/argocd-image-updater/values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
+1 -1
View File
@@ -9,7 +9,7 @@ spec:
project: homelab
sources:
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
path: k8s/apps/secret-rotation-controller
targetRevision: main
@@ -17,7 +17,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/minio/minio-operator-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -41,7 +41,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/minio
destination:
@@ -66,7 +66,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/longhorn
destination:
@@ -102,7 +102,7 @@ spec:
skipCrds: true
valueFiles:
- $values/k8s/infra/monitoring/prometheus-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -152,7 +152,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/monitoring/crds
destination:
@@ -183,7 +183,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/monitoring
destination:
@@ -213,7 +213,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/monitoring/blackbox-exporter-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -237,7 +237,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/tracing
destination:
+3 -3
View File
@@ -19,7 +19,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/logging/loki-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -53,7 +53,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/logging/grafana-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -87,7 +87,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/logging/promtail-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
+7 -28
View File
@@ -17,7 +17,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/iam/vault-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -46,7 +46,7 @@ spec:
helm:
valueFiles:
- $values/k8s/infra/iam/authentik-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -68,7 +68,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/iam
destination:
@@ -109,7 +109,7 @@ spec:
helm:
valueFiles:
- $values/k8s/bootstrap/phase3-forgejo/forgejo-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -152,17 +152,10 @@ metadata:
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "3"
argocd-image-updater.argoproj.io/image-list: runner=forgejo.riotpiao.com/rock/forgejo-runner-golang
argocd-image-updater.argoproj.io/runner.update-strategy: newest-build
argocd-image-updater.argoproj.io/runner.allow-tags: regexp:^[0-9a-f]{7}$|^latest$|^v[0-9]+$
argocd-image-updater.argoproj.io/runner.helm.image-name: runner.image.repository
argocd-image-updater.argoproj.io/runner.helm.image-tag: runner.image.tag
argocd-image-updater.argoproj.io/write-back-method: git
argocd-image-updater.argoproj.io/git-branch: main
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/forgejo-runner
destination:
@@ -180,17 +173,10 @@ metadata:
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "3"
argocd-image-updater.argoproj.io/image-list: runner=forgejo.riotpiao.com/rock/forgejo-runner-node
argocd-image-updater.argoproj.io/runner.update-strategy: newest-build
argocd-image-updater.argoproj.io/runner.allow-tags: regexp:^[0-9a-f]{7}$|^latest$|^v[0-9]+$
argocd-image-updater.argoproj.io/runner.helm.image-name: runner.image.repository
argocd-image-updater.argoproj.io/runner.helm.image-tag: runner.image.tag
argocd-image-updater.argoproj.io/write-back-method: git
argocd-image-updater.argoproj.io/git-branch: main
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/forgejo-runner
helm:
@@ -211,17 +197,10 @@ metadata:
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "3"
argocd-image-updater.argoproj.io/image-list: runner=forgejo.riotpiao.com/rock/forgejo-runner-rust
argocd-image-updater.argoproj.io/runner.update-strategy: newest-build
argocd-image-updater.argoproj.io/runner.allow-tags: regexp:^[0-9a-f]{7}$|^latest$|^v[0-9]+$
argocd-image-updater.argoproj.io/runner.helm.image-name: runner.image.repository
argocd-image-updater.argoproj.io/runner.helm.image-tag: runner.image.tag
argocd-image-updater.argoproj.io/write-back-method: git
argocd-image-updater.argoproj.io/git-branch: main
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/forgejo-runner
helm:
+1 -1
View File
@@ -14,7 +14,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/databases
destination:
+1 -1
View File
@@ -8,7 +8,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/messaging/memory-queues
destination:
+1 -1
View File
@@ -68,7 +68,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/messaging/kafka-cluster
destination:
+4 -4
View File
@@ -38,17 +38,17 @@ metadata:
argocd.argoproj.io/sync-wave: "7"
# ArgoCD Image Updater - auto-update on new image push
argocd-image-updater.argoproj.io/image-list: gw=forgejo.riotpiao.com/rock/api-gateway
argocd-image-updater.argoproj.io/gw.update-strategy: digest
argocd-image-updater.argoproj.io/gw.allow-tags: regexp:^latest$
argocd-image-updater.argoproj.io/gw.update-strategy: newest-build
argocd-image-updater.argoproj.io/gw.allow-tags: regexp:^[0-9a-f]{7}$
argocd-image-updater.argoproj.io/write-back-method: argocd
spec:
project: homelab
revisionHistoryLimit: 3
sources:
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab-frontend.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab-frontend.git
targetRevision: main
path: k8s
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/api
destination:
+1 -1
View File
@@ -20,7 +20,7 @@ spec:
project: homelab
revisionHistoryLimit: 3
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/llm-serving
destination:
-32
View File
@@ -1,32 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: comfyui
namespace: argocd
labels:
app.kubernetes.io/name: comfyui
app.kubernetes.io/component: image-generation
annotations:
argocd.argoproj.io/sync-wave: "8"
spec:
project: homelab
revisionHistoryLimit: 3
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
targetRevision: main
path: k8s/apps/comfyui
destination:
server: https://kubernetes.default.svc
namespace: comfyui
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
-32
View File
@@ -1,32 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gotify
namespace: argocd
labels:
app.kubernetes.io/name: gotify
app.kubernetes.io/component: notifications
annotations:
argocd.argoproj.io/sync-wave: "8"
spec:
project: homelab
revisionHistoryLimit: 3
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
targetRevision: main
path: k8s/apps/gotify
destination:
server: https://kubernetes.default.svc
namespace: notifications
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
+14 -15
View File
@@ -19,10 +19,10 @@ spec:
helm:
valueFiles:
- $values/k8s/apps/temporal/temporal-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/temporal
destination:
@@ -51,7 +51,7 @@ spec:
helm:
valueFiles:
- $values/k8s/apps/portainer/portainer-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
destination:
@@ -74,7 +74,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/cloudflared
destination:
@@ -97,7 +97,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/agent-pod
destination:
@@ -130,7 +130,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/sms
destination:
@@ -157,7 +157,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/paperless
destination:
@@ -189,7 +189,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/immich
destination:
@@ -220,10 +220,10 @@ spec:
helm:
valueFiles:
- $values/k8s/apps/homarr/homarr-values.yaml
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
ref: values
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/apps/homarr # PostSync hook: fix-probes-job.yaml
destination:
@@ -248,8 +248,8 @@ metadata:
argocd.argoproj.io/sync-wave: "8"
# ArgoCD Image Updater - auto-update on new image push
argocd-image-updater.argoproj.io/image-list: app=forgejo.riotpiao.com/rock/portfolio
argocd-image-updater.argoproj.io/app.update-strategy: digest
argocd-image-updater.argoproj.io/app.allow-tags: regexp:^latest$
argocd-image-updater.argoproj.io/app.update-strategy: newest-build
argocd-image-updater.argoproj.io/app.allow-tags: regexp:^[0-9a-f]{7}$
argocd-image-updater.argoproj.io/write-back-method: argocd
spec:
project: homelab
@@ -281,13 +281,12 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/infra/rbac
destination:
server: https://kubernetes.default.svc
# No namespace: cluster-scoped resources (ClusterRoleBinding, etc.)
# Namespace is set per-resource in kustomization
namespace: default
syncPolicy:
automated:
prune: true
+1 -1
View File
@@ -12,7 +12,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/kmsvc-manage.git
repoURL: https://forgejo.riotpiao.com/rock/kmsvc-manage.git
targetRevision: main
path: k8s/argocd/apps
directory:
-46
View File
@@ -1,46 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: poimen
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "7"
# Image Updater: auto-update on new image push (SHA tag filter)
argocd-image-updater.argoproj.io/image-list: |
memory=forgejo.riotpiao.com/rock/poimen-memory
workflows=forgejo.riotpiao.com/rock/poimen-workflows
frontend=forgejo.riotpiao.com/rock/poimen-frontend
argocd-image-updater.argoproj.io/memory.update-strategy: digest
argocd-image-updater.argoproj.io/memory.allow-tags: regexp:^latest$
argocd-image-updater.argoproj.io/workflows.update-strategy: digest
argocd-image-updater.argoproj.io/workflows.allow-tags: regexp:^latest$
argocd-image-updater.argoproj.io/frontend.update-strategy: digest
argocd-image-updater.argoproj.io/frontend.allow-tags: regexp:^latest$
argocd-image-updater.argoproj.io/write-back-method: argocd
spec:
project: homelab
sources:
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/poimen-memory.git
targetRevision: main
path: k8s/argocd
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/poimen-workflows.git
targetRevision: main
path: k8s/argocd
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/poimen-frontend.git
targetRevision: main
path: k8s/argocd
destination:
server: https://kubernetes.default.svc
namespace: poimen
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
+6 -7
View File
@@ -17,13 +17,12 @@ spec:
- https://github.com/Riotpiaole/Poimen-workflows.git
- https://github.com/Riotpiaole/poimen*.git
# In-cluster Forgejo repos — explicit allowlist (no wildcard)
- https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
- https://forgejo.riotpiao.com/riotpiao-poimen/homelab-frontend.git
- https://forgejo.riotpiao.com/riotpiao-poimen/kmsvc-manage.git
- https://forgejo.riotpiao.com/riotpiao-poimen/poimen.git
- https://forgejo.riotpiao.com/riotpiao-poimen/poimen-memory.git
- https://forgejo.riotpiao.com/riotpiao-poimen/poimen-workflows.git
- https://forgejo.riotpiao.com/riotpiao-poimen/poimen-frontend.git
- https://forgejo.riotpiao.com/rock/homelab.git
- https://forgejo.riotpiao.com/rock/homelab-frontend.git
- https://forgejo.riotpiao.com/rock/kmsvc-manage.git
- https://forgejo.riotpiao.com/rock/poimen.git
- https://forgejo.riotpiao.com/rock/poimen-memory.git
- https://forgejo.riotpiao.com/rock/poimen-workflows.git
- https://forgejo.riotpiao.com/rock/riotpiao.com.git
# Public Helm chart repos referenced by k8s/argocd/apps/* and bootstrap/*
- https://cloudnative-pg.github.io/charts
+1 -1
View File
@@ -12,7 +12,7 @@ metadata:
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
repoURL: https://forgejo.riotpiao.com/rock/homelab.git
targetRevision: main
path: k8s/argocd/apps
directory:
@@ -1,24 +0,0 @@
apiVersion: ENC[AES256_GCM,data:wR4=,iv:cRBzbvu0eUYCYeKeysua1/P3Meli/rQyj/mIV6VWnPM=,tag:oyTPA/mLYNUX+QDkYLlZyQ==,type:str]
kind: ENC[AES256_GCM,data:bdKQdadW,iv:F3DQiBI9xhSx87jkS1Hyevo48uUCBjsJSjbScIBznKA=,tag:PFakzzBj5S9F65dxu0L2rg==,type:str]
metadata:
name: ENC[AES256_GCM,data:XHFYrKClPo+IwRbM,iv:ZGuL+cN840Y1bOTC62NhDDcoZpTzDWQ4GfqPJX/QWmI=,tag:hlCVjzvfcxXGOASc3vda/Q==,type:str]
namespace: ENC[AES256_GCM,data:0BbhgZBog+1qY/iRJA==,iv:88tiSpEDqIokT5VP/d6bB2+aUyh1kZ7NEHwZWoJW3XU=,tag:CBR01jh2U9h7kNEcK1e1sA==,type:str]
type: ENC[AES256_GCM,data:Mpv1V73w,iv:BW3r6RpLnwe3XDKwrZySyOjrWUnSwIG5JOoPLzP/5gM=,tag:oyJySL0haOei1m4i+1AJ1g==,type:str]
stringData:
username: ENC[AES256_GCM,data:8xmxLGE=,iv:J/vEvXGoD+ka6FDgnSwDz0fs9IxuJIZW9r7Oyu2qxC8=,tag:dN9jd6NSavMN8+uzvemYWg==,type:str]
password: ENC[AES256_GCM,data:vB9PaaUiQZ8FY8pO0cq1fHLi7Gq5t4U=,iv:C0Y1m2SGYP3oTIFoau5JavVmLblj6te/SPMLodIwiZw=,tag:3Ip4YvR4WPzR0bBpTyjytA==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRT3JoTnhVVW5SUUJXbTdz
dUpLcmtBWWhyaGk3Y1hBM1ArcVI5eHREbmp3CiswUGVmd0NhejZwQ2UvNEdxS3ow
L1RweS9Kb2paeStLZ0tLSFdWbVZqQW8KLS0tIHJHT3hiMmxtM0Q1Ym5KOVpLVFpl
enR3NmdNdmdwVitTQVJlRHFWcjR0N2sKQw9ZZs+Ji/Zq/feO3qy4DwaCfWgDOQ/z
FVVhcCXweN58tb+9fzCJ+pNi/hSmvUkCMbb1+60qBvEehNzOoMRJ5g==
-----END AGE ENCRYPTED FILE-----
recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla
lastmodified: "2026-09-10T13:31:00Z"
mac: ENC[AES256_GCM,data:3HWV/NG0yTbsxY28u41zTRmAPc1kokGb4nCLmAsyq8/uvxcP+evDNyZXYpS93eVdPRfRZ1OqVBzyVGJEnj7JSXQVmlzor9JcWEL2fcpogoLVfJZKTRD6hk6optnBMjj84iQEEOx3A80JrDOdoXsH0pd9bJBABwoUOJwuufbXcIU=,iv:KLZsmAcapQgV08pFhGIvPt5ylsWg3xazAAjPuJYOaXA=,tag:1vKA3fISMIurHzxZ04F3lg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.2
@@ -1,30 +0,0 @@
apiVersion: ENC[AES256_GCM,data:xUA=,iv:YH1WYnoXhxbeES3Dmo8ZrMDwFcwCykt6+Zn4YSgN4gc=,tag:t3bIWo/LetlQv4tJ0nJ2Sw==,type:str]
kind: ENC[AES256_GCM,data:PJVzDTcT,iv:aRxvzLUuCzSDryOjA/v67csL6E2FcCpoK8aD7KNStno=,tag:cQgbowSTTV99nQ41yFOlsA==,type:str]
metadata:
name: ENC[AES256_GCM,data:atkSiP0rjyuWFjs=,iv:l/48X9W9lrkAyw6Q/p39V775L6xbr0tOIFI4vjYrg+E=,tag:LZrQxN5h6T5tehFG8L9KFw==,type:str]
namespace: ENC[AES256_GCM,data:fbklNyooBeqsogKbeg==,iv:z2wLgl81q8xfzGGVQ9A4Pkst/zLmuZpyBzHrXzmVfm4=,tag:QKJik4IWc4nrOBINjGCxvQ==,type:str]
type: ENC[AES256_GCM,data:bfAFBd/7,iv:9/W3X1KefqDAsurIatT6kBuEIQvWVRTq4G9A+9ZfYKs=,tag:Optlb+gZ2udbqIOerqV2zQ==,type:str]
stringData:
#ENC[AES256_GCM,data:RZaw/FNQZOf0F3+XfFmGW+4gjKXNdjrAbMRjBt4hptwk,iv:IEd/+Bsv/h08nKak+bcovNiohC7nNjEQ+QdtIFMUGng=,tag:zsF5ZQ3+5JijF4MU+RhnyA==,type:comment]
#ENC[AES256_GCM,data:ux3ONHnuI0uFEihElbLI1rKlrN91Pn0iLLZfmez0+t5d8yhH6OD3L/3oi6yHbdgD8bV90kRK/lw=,iv:7Ed+W/TeOY9Af8bKO0Fu3XOuaK8b0Hw70rwobfmosxM=,tag:Z0RbIOjdDroK0drC6P+Y8A==,type:comment]
host: ENC[AES256_GCM,data:YKgas/8hT5ZOGIW7O0c=,iv:WtBz1MFrwd6EgDDS5fMqtcVGIZGNLgbXnmkqlNWoXy8=,tag:+H9ARGPi5XocgZYp4oA3wA==,type:str]
port: ENC[AES256_GCM,data:f0hx,iv:HkSnJcCyzyfBTA39BNw+muhHTyrNLzzGxZ7M3bcLVu4=,tag:4+xL1xNyYPaV2b0mwnbQVQ==,type:str]
from: ENC[AES256_GCM,data:lqYqXxJuI4CAWPlX+5cltCdxqA==,iv:ZgZYySJXvjuTNIkMaDxynUJ6PFdp4fibqlgoA1vNDKE=,tag:vb/XKl2N1Hl5u7ndZXQvbg==,type:str]
user: ENC[AES256_GCM,data:ajljGVbRO//G,iv:E4Q0LGDCd5wnHc+Oq2A9ZhWGbCmkKlhllt922UmppIg=,tag:Cf4ft1JKFi9PaJEfPfq92Q==,type:str]
password: ENC[AES256_GCM,data:YNbtoiWLsQGl,iv:FJ3y1lWkqKkM8GVr7FZljAXJ331GkDgy4TenAbdIY3w=,tag:KIvhv+RL49ZMwWhYrU0SPA==,type:str]
notify-email: ENC[AES256_GCM,data:s5wRkDuaiflCyu4IyAnaocVPA3I=,iv:dK5PsweYa3JotYcDcz74DPNDXmhW5ngNEbY6JuG1ctg=,tag:/Dt2UoCtfau6qv0G7VzmrQ==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLQlJVekcwb01ESnhxSE9m
MzliVmlaYUttZVdiMDVtMlQ2L29LMXBWRzBZCkxRMi9RcE5LMzQ1MHFqUUxLaFZo
MUZ2UTg5eUNsMFVmenlhL2F3NDd0akkKLS0tIGFwbUUrSGFUTDFVOWlxM3V1bVdy
RUVVNDFMRHZCSWptdSt1bFVoN2JPWVkKXMlrJNPbm7Sj9vd/H3o6iqPRLy4LyjVe
xJyh9vn9RCwS9Jr6BN2Q9P2UZfTGwvIYc6zdmV+b2uRwvi4t4lMsBA==
-----END AGE ENCRYPTED FILE-----
recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla
lastmodified: "2026-09-10T13:31:00Z"
mac: ENC[AES256_GCM,data:g1YjOANlnvXErPqtDdDjRB9yTUiGEhX8QDkmf1aLfQBwoXbGUpoPIZibF8A4cAL1yUSZ2rD4l9TIe9nK8U3oF4WiKXf9u0bFK2g//bOv2A5JPewwmzoazkjIDXcJ/76nOQ5c+v8/r5tUG4c0SyzBH/gFP0D0+P5Yo3SEXjTxj2w=,iv:gW+AUwwAWkXZ+aLBXjjNDZw8VEraMpSE16RJGQc/L5g=,tag:DM/DUtil/7yLhfGYQXKFGA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.2
@@ -1,27 +0,0 @@
apiVersion: ENC[AES256_GCM,data:YE0=,iv:s3yNqcBn7/DKUQNgbGGwVmlM1HzxYGLBKyB6Y2ii2WE=,tag:77KxAjOFU3G0fSfrgudKkg==,type:str]
kind: ENC[AES256_GCM,data:PFW4VV9T,iv:n8gzMjE5C2TAfUTpp/8ex64B+hWUGG1K+2oQ4deN03Q=,tag:18Xqb6Di5izMHmzR5EU+QA==,type:str]
metadata:
name: ENC[AES256_GCM,data:Xhg1fQrD4itrbvDW0g==,iv:/THWSXAThb9fj+mm3wcxqzSdzdt7nE06+xOpkCxyImE=,tag:UChokr06VkbDZcFZDcUY2A==,type:str]
namespace: ENC[AES256_GCM,data:r25U5MuuzDd8JJ2YjQ==,iv:uDUcS4ZTpZe8HmZMArzkdu7LV5GUoLSP2wIs5HB1gv0=,tag:fci7j30hoxnVKJwPFNfd1Q==,type:str]
type: ENC[AES256_GCM,data:agEG8Fu8,iv:ckYX0buX8md1CWHXfxbAXQJLzoTxTJI16nlQ9LSzYi0=,tag:4tZWf3REbGOmmBiT14+dew==,type:str]
stringData:
#ENC[AES256_GCM,data:ehjcsmz1R0i/n31f8M0IIUT4KDBwzz6f5ds=,iv:j5swFAKpC67ZvGioiCCC1D651LxUl9gME8GqK5Uc+ys=,tag:BUJ8k9LHs8NAPPZAwPuifA==,type:comment]
#ENC[AES256_GCM,data:ndAB00yun636VHDMonqTghO/jCWodNd/hmdbm1QmCvOAi4FvTLl8bY3wYR+ZtlkSQYvFNqH798MJixv5OZwxBj5H,iv:pS+7B60jaS5jhqDRw2LbBFv7mD1HO6+hjjv+iNpenXc=,tag:NU6+gxCHTGxqeDMfvkwJWw==,type:comment]
#ENC[AES256_GCM,data:T3mhSm/5q7JTSXNLrjhpP5GhqA7nXz8uAhJcEV1RDctAX0Dyfq8Qn4bNFO51ibwTETx4SnunPuFZVs++AvRnsA==,iv:oRGM8N4iEcwBrMzoEXQAeKqCKzUq+jXTMVq9W2l6oh4=,tag:GrrbC/tkWpA5kp5UYCyRBA==,type:comment]
app-token: ENC[AES256_GCM,data:jw+AqbsxyoYRvNrUDrq+GNq/TNfweFCs,iv:67vSSgMZCMV0GG37ZxUxHAWZqOOGMYCmo3J43WgLyNk=,tag:xlrnip4XomXBbMmooW9FKg==,type:str]
client-token: ENC[AES256_GCM,data:gsxeCqKh4e+DFjpn9jM5GfemAdBf8dSv,iv:l2bBMdxQUil8jU9XDjXGn3EtvFVrK5ibXDCeGaPbYTY=,tag:ELf5S6cdNQJ84Es5upu1IQ==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAza3YrRW1VTVNSOGlMK21a
bDhzQlEyZDNnZTZrSVNnTUEzU1hSdnM1ZFVzCjdBSUlmWG5EcnlzbSs5bXdDaGkx
ME1nMnRqQzF1Vkc3b3FXSUVHT1g0ZDgKLS0tIHpMUytEMjdLQ2E0Unp2di9KS3JQ
eHBraDk1clJyanhLY2dGM0tESmJIUFkKHTl3y9uQiEofOFD8j2vH3YK/CVzlq11w
GfShIji1yCvvowKGzYYhsQK0UM0FzhzBv0GFMYWQCBq8pGdoPVmO5g==
-----END AGE ENCRYPTED FILE-----
recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla
lastmodified: "2026-09-10T13:31:00Z"
mac: ENC[AES256_GCM,data:JhnO0V6cd2QVY/VhwHAJ5J75GeVlOVHBfVBTZstkj/IvpkAcwS/JE2b6jXiCwEC4n/vdA8DJ074noysUBRxh4Y7O4NKuvWcTniQKgqULNL1Hzgj3NdUkcQlWT+Z0HQ7FlvFH97VVXVfasU+CLHG48ShT2fDSj8JusEllb9CwSvg=,iv:PZo2krxvJc1TLJbvx+S9ClthoBe4w7WigUkq7dg0gNk=,tag:2IF5g/WTRP4XdnCZzDbiIA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.2
-3
View File
@@ -27,6 +27,3 @@ files:
- vault-secrets.enc.yaml
- vault-unseal-keys.enc.yaml
- portfolio-secrets.enc.yaml
- gotify-admin-secrets.enc.yaml
- gotify-tokens-secrets.enc.yaml
- gotify-smtp-secrets.enc.yaml
@@ -20,47 +20,10 @@ config:
insecure: true
plaintext: true
# Git write-back configuration (for multi-source Applications)
git:
# Commit author for image updates
user:
name: "ArgoCD Image Updater"
email: "[email protected]"
# Use SSH keys from ArgoCD's known hosts + credentials
# Image Updater inherits ArgoCD's git credentials (mounted via ArgoCD secret)
# Mount ArgoCD's git credentials for write-back
extraVolumes:
- name: argocd-ssh-known-hosts-cm
configMap:
name: argocd-ssh-known-hosts-cm
defaultMode: 0644
- name: argocd-gpg-keys-cm
configMap:
name: argocd-gpg-keys-cm
optional: true
defaultMode: 0644
- name: argocd-gpg-pubring
configMap:
name: argocd-gpg-pubring-cm
optional: true
defaultMode: 0644
extraVolumeMounts:
- name: argocd-ssh-known-hosts-cm
mountPath: /etc/ssh/ssh_known_hosts.d/argocd-ssh-known-hosts
subPath: ssh_known_hosts
- name: argocd-gpg-keys-cm
mountPath: /etc/gpg/source
- name: argocd-gpg-pubring
mountPath: /etc/gpg/pubring
# Extra environment variables
extraEnv:
- name: ARGOCD_GRPC_WEB
value: "true"
- name: GIT_SSH_KNOWN_HOSTS_CONFIG_MAP_ENABLED
value: "true"
# Resources
resources:
+1 -1
View File
@@ -11,7 +11,7 @@ metadata:
annotations:
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
instances: 3
instances: 2
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
bootstrap:
initdb:
@@ -36,4 +36,3 @@ data:
valid_volumes:
- /docker-certs/client
network: host
docker_host: automount
@@ -34,11 +34,7 @@ spec:
command: ["sh", "-c"]
args:
- |
# Always re-register to keep labels in sync with values.yaml.
# Without this, changing a runner label requires manually deleting
# the PVC or .runner file — not GitOps-friendly.
rm -f /data/.runner
forgejo-runner register --no-interactive \
test -f /data/.runner || forgejo-runner register --no-interactive \
--instance {{ .Values.runner.forgejoUrl }} \
--token $(RUNNER_TOKEN) \
--name {{ .Values.runner.name }} \
@@ -60,18 +56,20 @@ spec:
containers:
- name: runner
image: {{ .Values.runner.image.repository }}:{{ .Values.runner.image.tag }}
command: ["sh", "-c", "while ! wget -q -O- http://localhost:2375/_ping >/dev/null 2>&1; do echo 'waiting for dind...'; sleep 2; done; echo 'dind ready'; forgejo-runner daemon --config /etc/forgejo-runner/config.yaml"]
command: ["sh", "-c", "forgejo-runner daemon --config /etc/forgejo-runner/config.yaml"]
workingDir: /data
env:
- name: DOCKER_HOST
value: tcp://localhost:2375
value: tcp://localhost:2376
- name: DOCKER_TLS_VERIFY
value: "1"
- name: DOCKER_CERT_PATH
value: /docker-certs/client
volumeMounts:
- name: runner-data
mountPath: /data
- name: docker-certs
mountPath: /docker-certs
- name: docker-sock
mountPath: /run
- name: homelab-ca
mountPath: /etc/ssl/certs/homelab-ca.pem
subPath: ca.crt
@@ -87,12 +85,10 @@ spec:
privileged: true # required for DinD; cicd namespace is labelled privileged
env:
- name: DOCKER_TLS_CERTDIR
value: ""
value: /docker-certs
volumeMounts:
- name: docker-certs
mountPath: /docker-certs
- name: docker-sock
mountPath: /run
- name: dind-storage
mountPath: /var/lib/docker
- name: homelab-ca
@@ -117,8 +113,6 @@ spec:
claimName: {{ .Release.Name }}-dind
- name: docker-certs
emptyDir: {} # DinD regenerates mTLS certs on each start
- name: docker-sock
emptyDir: {} # Shared docker socket between dind and runner
- name: homelab-ca
# homelab-ca is a ConfigMap (public CA trust bundle), not a Secret.
# The volumeMounts use subPath: ca.crt to project the single cert file.
+2 -5
View File
@@ -2,12 +2,9 @@
# runner instance. Only runner.name and runner.labels differ -- everything
# else (image, dind, persistence, tolerations, nodeSelector) is shared.
#
# Label image: node:22-bookworm — Debian, root, apt-get, Node.js, npm, git.
# Install docker in workflow steps as needed.
# node:22-bookworm ships Node natively, so unlike the golang/rust instances,
# jobs on this runner need no "install node" step before actions/checkout.
runner:
image:
repository: code.forgejo.org/forgejo/runner
tag: "6"
name: node-runner
labels: "node:docker://node:22-bookworm"
+8 -7
View File
@@ -2,16 +2,17 @@
# runner instance. Only runner.name and runner.labels differ -- everything
# else (image, dind, persistence, tolerations, nodeSelector) is shared.
#
# Label image: rust:1-bookworm — Debian, root, apt-get, Rust, cargo, git.
# Install Node.js/docker in workflow steps as needed.
# rust:1.83-bookworm -- verified this tag exists (docker manifest inspect)
# before pinning it, per this repo's convention of not trusting a tag exists
# without checking.
runner:
image:
repository: code.forgejo.org/forgejo/runner
tag: "6"
name: rust-runner
labels: "rust:docker://rust:1-bookworm"
labels: "rust:docker://rust:1.83-bookworm"
persistence:
reg:
storageClass: longhorn
size: 20Gi
# GC CronJob renders only from the default (golang) values to avoid duplicates
gc:
+13 -6
View File
@@ -1,13 +1,20 @@
runner:
image:
repository: code.forgejo.org/forgejo/runner
tag: "6"
tag: "6" # pin exact release before apply
name: golang-runner
# Label image is what workflow steps run in (NOT the runner daemon image).
# golang:1.26-bookworm: Debian, root, apt-get, Go, git.
# TODO: Switch to custom image once build-runner-images.yml pushes images
# Default image is only used when a job's `container:` doesn't override it
# (both ci.yaml and build.yaml in homelab-frontend do). Retired the old
# "docker" label entirely; every repo this runner serves is Go, so this
# instance carries the golang toolchain and its own dind sidecar builds and
# pushes that repo's images too -- there is no separate generic runner
# anymore.
labels: "golang:docker://golang:1.26-bookworm"
# In-cluster Service (:3000) — direct, avoids the ingress/public-hostname hop
# (the public URL is :443 which forgejo doesn't serve; runner got i/o timeout).
forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000
# tokenSecret: name of the K8s Secret that holds the runner registration token
# created automatically by the helmfile presync hook (see helmfile.yaml.gotmpl)
tokenSecret: runner-token
resources:
requests:
@@ -54,6 +61,6 @@ gc:
enabled: true
schedule: "*/30 * * * *" # every 30 minutes
image: alpine/k8s:1.31.0
pruneAge: "30m" # Docker artifacts unused longer than this get pruned
pruneAgeHours: 0.5 # Same as pruneAge but numeric for date arithmetic in shell
pruneAge: "72h" # Docker artifacts unused longer than this get pruned
pruneAgeHours: 72 # Same as pruneAge but numeric for date arithmetic in shell
actcacheMaxAgeDays: 1 # actcache files older than N days (aggressive for heavy Rust cargo builds)
-15
View File
@@ -1,15 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: homelab-admin-oidc
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
# OIDC group for Authentik homelab-admins members
# When rock logs in via OIDC, k8s sees:
# - User: oidc:[email protected]
# - Groups: oidc:homelab-admins (from Authentik group claim)
- kind: Group
name: oidc:homelab-admins
-1
View File
@@ -6,7 +6,6 @@ kind: Kustomization
# already fixed once in k8s/infra/minio and k8s/infra/iam. Every resource
# here sets its own explicit metadata.namespace.
resources:
- admin-oidc-binding.yaml
- grafana-operator-role.yaml
- minio-operator-role.yaml
- forgejo-operator-role.yaml
File diff suppressed because it is too large Load Diff
-2
View File
@@ -35,8 +35,6 @@
rewrite name longhorn.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
rewrite name paperless.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
rewrite name img.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
rewrite name api.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
rewrite name comfy.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
rewrite name riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
kubernetes cluster.local in-addr.arpa ip6.arpa {