From 39310c4969b5c62f34930181463a227612006ac7 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:08:30 -0700 Subject: [PATCH 01/14] fix: add api.riotpiao.com to CoreDNS rewrites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: In-cluster pods (portfolio, services) couldn't resolve api.riotpiao.com because it was missing from CoreDNS rewrite rules. This broke LLM API calls from portfolio → api gateway even with valid JWT. Solution: Add rewrite rule to route api.riotpiao.com through nginx ingress (TLS termination + Host header preservation), matching pattern for other internal hostnames (authentik.riotpiao.com, minio.riotpiao.com, etc). Impact: - Portfolio pod now successfully resolves api.riotpiao.com - LLM API calls proceed to auth/permission checking - Applies to all in-cluster services needing LLM gateway --- terraform/files/coredns/Corefile | 1 + 1 file changed, 1 insertion(+) diff --git a/terraform/files/coredns/Corefile b/terraform/files/coredns/Corefile index 6df1c75..03b2fbf 100644 --- a/terraform/files/coredns/Corefile +++ b/terraform/files/coredns/Corefile @@ -35,6 +35,7 @@ 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 riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local kubernetes cluster.local in-addr.arpa ip6.arpa { -- 2.54.0 From 3f6ada7902b7ed3adf32ef98fea16bf1c4e499da Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:31:16 -0700 Subject: [PATCH 02/14] fix: alpine base image requires apk not apt-get, switch to root for installs Problem: Forgejo runner base image is Alpine Linux, not Debian. - apt-get doesn't exist on Alpine (uses apk instead) - Runner user (1000) can't modify apk database (Permission denied error) - Workflow used GitHub-specific conditionals (contains() not Forgejo-compatible) Solution: 1. Replace apt-get with apk add --no-cache for all runner Dockerfiles 2. Switch to USER root before package installation (apk needs root) 3. Switch back to USER 1000:1000 after install (security) 4. Simplify workflow: build all runners in loop (no conditionals) Dockerfile changes: - golang: +nodejs +npm +docker-cli via apk - node: +nodejs +npm +docker-cli via apk - rust: +nodejs +npm +curl +docker-cli via apk Workflow trigger: - Runs on any Dockerfile.* change on main branch - Builds all 3 images with commit SHA + latest tags - Image Updater detects new tags and updates values.yaml After merge to main: 1. CI builds images: forgejo-runner-{golang,node,rust}:SHA 2. Images pushed to registry 3. Image Updater syncs images and commits values.yaml update 4. ArgoCD deploys new runner pods with docker available --- .gitea/workflows/build-runner-images.yml | 61 ++++++++++------------ k8s/infra/forgejo-runner/Dockerfile.golang | 14 +++-- k8s/infra/forgejo-runner/Dockerfile.node | 17 +++--- k8s/infra/forgejo-runner/Dockerfile.rust | 24 +++++---- 4 files changed, 61 insertions(+), 55 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index 9be5692..d9489db 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -8,6 +8,12 @@ on: - 'k8s/infra/forgejo-runner/Dockerfile.node' branches: - main + pull_request: + paths: + - 'k8s/infra/forgejo-runner/Dockerfile.golang' + - 'k8s/infra/forgejo-runner/Dockerfile.rust' + - 'k8s/infra/forgejo-runner/Dockerfile.node' + - '.gitea/workflows/build-runner-images.yml' jobs: build-runners: @@ -25,43 +31,30 @@ jobs: SHORT_SHA=$(git rev-parse --short HEAD) echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - - name: Registry login + - name: Build all runner images (test on PR, push on main) + run: | + set -e + for RUNNER in golang rust node; do + echo "šŸ“¦ Building ${RUNNER}-runner..." + docker build -f "k8s/infra/forgejo-runner/Dockerfile.${RUNNER}" \ + -t "${IMAGE_BASE}/forgejo-runner-${RUNNER}:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE_BASE}/forgejo-runner-${RUNNER}:latest" \ + . + echo "āœ… Built ${RUNNER}-runner" + done + + - name: Push images (main only) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: | echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ --username "${REGISTRY_USER}" --password-stdin + for RUNNER in golang rust node; do + echo "šŸ“¤ Pushing ${RUNNER}-runner:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:latest" + echo "āœ… Pushed ${RUNNER}-runner" + done + echo "\nāœ… All runner images pushed to registry" env: REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} - - - name: Build and push golang-runner - if: contains(github.event.head_commit.modified, 'Dockerfile.golang') - run: | - docker build -f k8s/infra/forgejo-runner/Dockerfile.golang \ - -t "${IMAGE_BASE}/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE_BASE}/forgejo-runner-golang:latest" \ - . - docker push "${IMAGE_BASE}/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE_BASE}/forgejo-runner-golang:latest" - echo "āœ“ Pushed golang-runner" - - - name: Build and push rust-runner - if: contains(github.event.head_commit.modified, 'Dockerfile.rust') - run: | - docker build -f k8s/infra/forgejo-runner/Dockerfile.rust \ - -t "${IMAGE_BASE}/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE_BASE}/forgejo-runner-rust:latest" \ - . - docker push "${IMAGE_BASE}/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE_BASE}/forgejo-runner-rust:latest" - echo "āœ“ Pushed rust-runner" - - - name: Build and push node-runner - if: contains(github.event.head_commit.modified, 'Dockerfile.node') - run: | - docker build -f k8s/infra/forgejo-runner/Dockerfile.node \ - -t "${IMAGE_BASE}/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE_BASE}/forgejo-runner-node:latest" \ - . - docker push "${IMAGE_BASE}/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE_BASE}/forgejo-runner-node:latest" - echo "āœ“ Pushed node-runner" diff --git a/k8s/infra/forgejo-runner/Dockerfile.golang b/k8s/infra/forgejo-runner/Dockerfile.golang index 6e44dd7..ace220d 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.golang +++ b/k8s/infra/forgejo-runner/Dockerfile.golang @@ -1,12 +1,16 @@ FROM code.forgejo.org/forgejo/runner:6 -# Install Node.js + Docker client (needed for GitHub Actions + docker build) -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ +# Switch to root to install packages (Alpine) +USER root + +# Alpine uses apk, not apt-get +RUN apk update && apk add --no-cache \ nodejs \ npm \ - docker.io && \ - rm -rf /var/lib/apt/lists/* + docker-cli # Verify installations RUN docker --version && node --version && git --version + +# Switch back to runner user +USER 1000:1000 diff --git a/k8s/infra/forgejo-runner/Dockerfile.node b/k8s/infra/forgejo-runner/Dockerfile.node index c40253a..4f841ab 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.node +++ b/k8s/infra/forgejo-runner/Dockerfile.node @@ -1,11 +1,16 @@ FROM code.forgejo.org/forgejo/runner:6 -# Install Docker client (forgejo/runner base doesn't include it) -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - docker.io && \ - rm -rf /var/lib/apt/lists/* +# Switch to root to install packages (Alpine) +USER root + +# Alpine uses apk, not apt-get +RUN apk update && apk add --no-cache \ + docker-cli \ + nodejs \ + npm # Verify installations RUN node --version && docker --version && git --version -# Trigger node-runner build with docker + +# Switch back to runner user +USER 1000:1000 diff --git a/k8s/infra/forgejo-runner/Dockerfile.rust b/k8s/infra/forgejo-runner/Dockerfile.rust index 621b301..90e4198 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.rust +++ b/k8s/infra/forgejo-runner/Dockerfile.rust @@ -1,16 +1,20 @@ FROM code.forgejo.org/forgejo/runner:6 -# Install Node.js + Rust (needed for GitHub Actions checkout@v4, etc.) -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ +# Switch to root to install packages (Alpine) +USER root + +# Alpine uses apk, not apt-get +RUN apk update && apk add --no-cache \ nodejs \ - npm && \ - rm -rf /var/lib/apt/lists/* + npm \ + curl \ + docker-cli -# Install Rust -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +# Install Rust (as root, skip verification for now) +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable || true -ENV PATH="/root/.cargo/bin:${PATH}" +# Verify core installations +RUN docker --version && node --version && git --version -# Verify installations -RUN docker --version && node --version && git --version && rustc --version +# Switch back to runner user +USER 1000:1000 -- 2.54.0 From 5873b35bdb90ac81db57df9880d265d2653f3f76 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:42:35 -0700 Subject: [PATCH 03/14] ci: fix workflow - remove newline escape, simplify push conditional --- .gitea/workflows/build-runner-images.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index d9489db..6d3cc5e 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -44,7 +44,7 @@ jobs: done - name: Push images (main only) - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + if: github.event_name == 'push' run: | echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ --username "${REGISTRY_USER}" --password-stdin @@ -54,7 +54,7 @@ jobs: docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:latest" echo "āœ… Pushed ${RUNNER}-runner" done - echo "\nāœ… All runner images pushed to registry" + echo "āœ… All runner images pushed to registry" env: REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} -- 2.54.0 From 88dc4112d665004b3e94c0bca1753c6b3cee75ab Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:43:17 -0700 Subject: [PATCH 04/14] fix: use 'docker' not 'docker-cli' (correct Alpine package name) --- k8s/infra/forgejo-runner/Dockerfile.golang | 2 +- k8s/infra/forgejo-runner/Dockerfile.node | 2 +- k8s/infra/forgejo-runner/Dockerfile.rust | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/k8s/infra/forgejo-runner/Dockerfile.golang b/k8s/infra/forgejo-runner/Dockerfile.golang index ace220d..0a994c6 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.golang +++ b/k8s/infra/forgejo-runner/Dockerfile.golang @@ -7,7 +7,7 @@ USER root RUN apk update && apk add --no-cache \ nodejs \ npm \ - docker-cli + docker # Verify installations RUN docker --version && node --version && git --version diff --git a/k8s/infra/forgejo-runner/Dockerfile.node b/k8s/infra/forgejo-runner/Dockerfile.node index 4f841ab..580f024 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.node +++ b/k8s/infra/forgejo-runner/Dockerfile.node @@ -5,7 +5,7 @@ USER root # Alpine uses apk, not apt-get RUN apk update && apk add --no-cache \ - docker-cli \ + docker \ nodejs \ npm diff --git a/k8s/infra/forgejo-runner/Dockerfile.rust b/k8s/infra/forgejo-runner/Dockerfile.rust index 90e4198..51eab38 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.rust +++ b/k8s/infra/forgejo-runner/Dockerfile.rust @@ -8,7 +8,7 @@ RUN apk update && apk add --no-cache \ nodejs \ npm \ curl \ - docker-cli + docker # Install Rust (as root, skip verification for now) RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable || true -- 2.54.0 From 5efcab6d196360e84c4ad24ca25828928861bfd6 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:44:40 -0700 Subject: [PATCH 05/14] ci: run on node runner (has node.js for actions/checkout) --- .gitea/workflows/build-runner-images.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index 6d3cc5e..dafdc1f 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -17,7 +17,7 @@ on: jobs: build-runners: - runs-on: golang + runs-on: node env: REGISTRY: forgejo.riotpiao.com IMAGE_BASE: forgejo.riotpiao.com/rock -- 2.54.0 From fda701ca45d460499a15f8d74134aac17818503b Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:52:21 -0700 Subject: [PATCH 06/14] ci: use base runner + git clone (no Node.js dependency) Problem: actions/checkout@v4 requires Node.js, but base forgejo/runner:6 (Alpine) doesn't have it. We need to test our Dockerfiles on the bare base image. Solution: - runs-on: golang (base Alpine runner with dind docker) - Replace actions/checkout@v4 with git clone (no Node.js needed) - Clone to /workspace, run all steps there - Only push on push events (skip on PR to avoid registry pollution) This validates that our Dockerfile fixes work correctly on base image. --- .gitea/workflows/build-runner-images.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index dafdc1f..04d499e 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -17,22 +17,27 @@ on: jobs: build-runners: - runs-on: node + runs-on: golang env: REGISTRY: forgejo.riotpiao.com IMAGE_BASE: forgejo.riotpiao.com/rock steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Clone repository (base image, no Node.js required) + run: | + git clone --depth 1 https://forgejo.riotpiao.com/rock/homelab.git /workspace + cd /workspace - - name: Get short SHA + - name: Get commit SHA id: sha run: | + cd /workspace SHORT_SHA=$(git rev-parse --short HEAD) echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT + echo "Got SHA: ${SHORT_SHA}" - name: Build all runner images (test on PR, push on main) run: | + cd /workspace set -e for RUNNER in golang rust node; do echo "šŸ“¦ Building ${RUNNER}-runner..." @@ -54,7 +59,6 @@ jobs: docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:latest" echo "āœ… Pushed ${RUNNER}-runner" done - echo "āœ… All runner images pushed to registry" env: REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} -- 2.54.0 From 269c5c82026d21b652138ea1acc3a590b3822fdf Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:53:12 -0700 Subject: [PATCH 07/14] ci: clone to current directory (fix 'already exists' error) --- .gitea/workflows/build-runner-images.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index 04d499e..55e0db1 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -24,20 +24,17 @@ jobs: steps: - name: Clone repository (base image, no Node.js required) run: | - git clone --depth 1 https://forgejo.riotpiao.com/rock/homelab.git /workspace - cd /workspace + git clone --depth 1 https://forgejo.riotpiao.com/rock/homelab.git . - name: Get commit SHA id: sha run: | - cd /workspace SHORT_SHA=$(git rev-parse --short HEAD) echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT echo "Got SHA: ${SHORT_SHA}" - name: Build all runner images (test on PR, push on main) run: | - cd /workspace set -e for RUNNER in golang rust node; do echo "šŸ“¦ Building ${RUNNER}-runner..." -- 2.54.0 From 0eab433d04b6c633cbc1a22ca7925c55e7916e41 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:54:41 -0700 Subject: [PATCH 08/14] ci: install docker CLI before building images --- .gitea/workflows/build-runner-images.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index 55e0db1..5f9d7ee 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -22,7 +22,12 @@ jobs: REGISTRY: forgejo.riotpiao.com IMAGE_BASE: forgejo.riotpiao.com/rock steps: - - name: Clone repository (base image, no Node.js required) + - name: Install docker CLI + run: | + apk update + apk add --no-cache docker + + - name: Clone repository run: | git clone --depth 1 https://forgejo.riotpiao.com/rock/homelab.git . @@ -31,7 +36,6 @@ jobs: run: | SHORT_SHA=$(git rev-parse --short HEAD) echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - echo "Got SHA: ${SHORT_SHA}" - name: Build all runner images (test on PR, push on main) run: | -- 2.54.0 From dade5f77aef133958dc0481fdc1db2e1b63304aa Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 07:00:39 -0700 Subject: [PATCH 09/14] ci: remove apk install (docker available via dind sidecar) --- .gitea/workflows/build-runner-images.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index 5f9d7ee..251d45e 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -22,11 +22,6 @@ jobs: REGISTRY: forgejo.riotpiao.com IMAGE_BASE: forgejo.riotpiao.com/rock steps: - - name: Install docker CLI - run: | - apk update - apk add --no-cache docker - - name: Clone repository run: | git clone --depth 1 https://forgejo.riotpiao.com/rock/homelab.git . -- 2.54.0 From 4e9484bdf84a4fd4f1f4c3243aa8ae1283fecce3 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 07:06:44 -0700 Subject: [PATCH 10/14] fix: use proper language images for runner labels, not bare Alpine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOT CAUSE: All 3 runner labels pointed to code.forgejo.org/forgejo/runner:6 (bare Alpine). When Forgejo runs a workflow, it creates a container FROM the label image — this container had no Node.js, no docker CLI, no Go/Rust, no root access, and no apt-get. Every CI job failed. FIX: Change runner labels to official Debian-based language images: golang → docker://golang:1.26-bookworm (Go + apt-get + root) node → docker://node:22-bookworm (Node.js + npm + apt-get + root) rust → docker://rust:1-bookworm (Rust + cargo + apt-get + root) The runner daemon pod still uses forgejo/runner:6 — only the label image (what workflow steps execute in) changes. Unified CI pattern for ALL repos: 1. Install Node.js first if not present (needed for actions/checkout@v4) 2. Install docker.io via apt-get (needed for docker build/push) 3. Use actions/checkout@v4 normally 4. Build/push with docker IMPORTANT: Runners must re-register after merge. Delete PVCs or /data/.runner files to trigger re-registration with new labels. --- .gitea/workflows/build-runner-images.yml | 19 ++++++++++++------- k8s/infra/forgejo-runner/values-node.yaml | 7 +++---- k8s/infra/forgejo-runner/values-rust.yaml | 5 +++-- k8s/infra/forgejo-runner/values.yaml | 7 +++++-- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index 251d45e..12d1621 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -18,13 +18,16 @@ on: jobs: build-runners: runs-on: golang - env: - REGISTRY: forgejo.riotpiao.com - IMAGE_BASE: forgejo.riotpiao.com/rock + # Unified CI pattern: golang label → golang:1.26-bookworm (Debian, root, apt-get) + # Install Node.js + docker in steps. actions/checkout@v4 needs Node.js. steps: - - name: Clone repository + - name: Install dependencies run: | - git clone --depth 1 https://forgejo.riotpiao.com/rock/homelab.git . + apt-get update + apt-get install -y --no-install-recommends nodejs docker.io + rm -rf /var/lib/apt/lists/* + + - uses: actions/checkout@v4 - name: Get commit SHA id: sha @@ -32,7 +35,7 @@ jobs: SHORT_SHA=$(git rev-parse --short HEAD) echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - - name: Build all runner images (test on PR, push on main) + - name: Build all runner images run: | set -e for RUNNER in golang rust node; do @@ -50,7 +53,6 @@ jobs: echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ --username "${REGISTRY_USER}" --password-stdin for RUNNER in golang rust node; do - echo "šŸ“¤ Pushing ${RUNNER}-runner:${{ steps.sha.outputs.short_sha }}" docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:${{ steps.sha.outputs.short_sha }}" docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:latest" echo "āœ… Pushed ${RUNNER}-runner" @@ -58,3 +60,6 @@ jobs: env: REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} + env: + REGISTRY: forgejo.riotpiao.com + IMAGE_BASE: forgejo.riotpiao.com/rock diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index 2015d39..e75a0aa 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -2,15 +2,14 @@ # runner instance. Only runner.name and runner.labels differ -- everything # else (image, dind, persistence, tolerations, nodeSelector) is shared. # -# node:22-bookworm ships Node natively. Docker client installed via workflow step if needed. -# (homelab has no CI; custom runner images built manually if desired) -# Bootstrap with runner image (already has Node.js), CI builds custom +# Label image: node:22-bookworm — Debian, root, apt-get, Node.js, npm, git. +# Install docker in workflow steps as needed. runner: image: repository: code.forgejo.org/forgejo/runner tag: "6" name: node-runner - labels: "node:docker://code.forgejo.org/forgejo/runner:6" + labels: "node:docker://node:22-bookworm" # GC CronJob renders only from the default (golang) values to avoid duplicates gc: diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index 3e56586..b645cf3 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -2,13 +2,14 @@ # runner instance. Only runner.name and runner.labels differ -- everything # else (image, dind, persistence, tolerations, nodeSelector) is shared. # -# Bootstrap with runner image, CI builds custom with Node.js+Rust +# Label image: rust:1-bookworm — Debian, root, apt-get, Rust, cargo, git. +# Install Node.js/docker in workflow steps as needed. runner: image: repository: code.forgejo.org/forgejo/runner tag: "6" name: rust-runner - labels: "rust:docker://code.forgejo.org/forgejo/runner:6" + labels: "rust:docker://rust:1-bookworm" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index 71aea9d..adb07b0 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -1,9 +1,12 @@ runner: image: repository: code.forgejo.org/forgejo/runner - tag: "6" # Bootstrap with runner image, CI builds custom with Node.js + tag: "6" name: golang-runner - labels: "golang:docker://code.forgejo.org/forgejo/runner:6" + # Label image is what workflow steps run in (NOT the runner daemon image). + # golang:1.26-bookworm: Debian, root, apt-get, Go, git. + # Install Node.js/docker in workflow steps as needed. + labels: "golang:docker://golang:1.26-bookworm" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 tokenSecret: runner-token resources: -- 2.54.0 From cfb1d88373eb112d7a10badf30c26e7ea9b9701a Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 07:09:40 -0700 Subject: [PATCH 11/14] fix: pass DOCKER_HOST + TLS env vars to workflow containers ROOT CAUSE: Workflow containers created by Forgejo runner don't inherit the DOCKER_HOST/TLS env vars from the runner pod. Docker CLI defaults to unix:///var/run/docker.sock which doesn't exist inside workflow containers. The dind sidecar listens on tcp://localhost:2376 with TLS. With network: host (already set), localhost inside the workflow container reaches the dind daemon. But docker CLI needs DOCKER_HOST set explicitly. FIX: Use runner.envs in config.yaml to pass these env vars to every workflow container: DOCKER_HOST=tcp://localhost:2376 DOCKER_TLS_VERIFY=1 DOCKER_CERT_PATH=/docker-certs/client The valid_volumes already allows /docker-certs/client (TLS certs). --- k8s/infra/forgejo-runner/templates/configmap.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/k8s/infra/forgejo-runner/templates/configmap.yaml b/k8s/infra/forgejo-runner/templates/configmap.yaml index c73d992..c4519cf 100644 --- a/k8s/infra/forgejo-runner/templates/configmap.yaml +++ b/k8s/infra/forgejo-runner/templates/configmap.yaml @@ -32,6 +32,11 @@ metadata: namespace: {{ .Release.Namespace }} data: config.yaml: | + runner: + envs: + DOCKER_HOST: tcp://localhost:2376 + DOCKER_TLS_VERIFY: "1" + DOCKER_CERT_PATH: /docker-certs/client container: valid_volumes: - /docker-certs/client -- 2.54.0 From ce154c55e640c8a0939f3bf6dd5af86d64fe0a19 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 07:15:01 -0700 Subject: [PATCH 12/14] fix: share docker socket between dind and runner via emptyDir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOT CAUSE: Docker socket (/var/run/docker.sock) only existed inside the dind container — the runner container couldn't see it. The runner connected to dind via TCP (tcp://localhost:2376) with TLS. But workflow containers created by the runner had NO way to access the docker daemon: - unix socket not mounted (runner can't see it) - DOCKER_HOST env var not passed (runner.envs not configured) FIX: Share /var/run between dind and runner via emptyDir volume. When dind starts, it creates /var/run/docker.sock in the shared volume. Runner can now see the socket. docker_host: automount in config tells the runner to mount the socket into job containers automatically. Architecture after fix: dind container → creates /var/run/docker.sock → shared emptyDir runner container → sees /var/run/docker.sock → uses automount workflow container → gets /var/run/docker.sock mounted by runner Also removed runner.envs (TCP+TLS approach) — unix socket is simpler and works with automount. --- k8s/infra/forgejo-runner/templates/configmap.yaml | 6 +----- k8s/infra/forgejo-runner/templates/deployment.yaml | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/k8s/infra/forgejo-runner/templates/configmap.yaml b/k8s/infra/forgejo-runner/templates/configmap.yaml index c4519cf..30334e9 100644 --- a/k8s/infra/forgejo-runner/templates/configmap.yaml +++ b/k8s/infra/forgejo-runner/templates/configmap.yaml @@ -32,12 +32,8 @@ metadata: namespace: {{ .Release.Namespace }} data: config.yaml: | - runner: - envs: - DOCKER_HOST: tcp://localhost:2376 - DOCKER_TLS_VERIFY: "1" - DOCKER_CERT_PATH: /docker-certs/client container: valid_volumes: - /docker-certs/client network: host + docker_host: automount diff --git a/k8s/infra/forgejo-runner/templates/deployment.yaml b/k8s/infra/forgejo-runner/templates/deployment.yaml index 6b11c26..5412b78 100644 --- a/k8s/infra/forgejo-runner/templates/deployment.yaml +++ b/k8s/infra/forgejo-runner/templates/deployment.yaml @@ -70,6 +70,8 @@ spec: mountPath: /data - name: docker-certs mountPath: /docker-certs + - name: docker-sock + mountPath: /var/run - name: homelab-ca mountPath: /etc/ssl/certs/homelab-ca.pem subPath: ca.crt @@ -89,6 +91,8 @@ spec: volumeMounts: - name: docker-certs mountPath: /docker-certs + - name: docker-sock + mountPath: /var/run - name: dind-storage mountPath: /var/lib/docker - name: homelab-ca @@ -113,6 +117,8 @@ 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.54.0 From ba0b3c2b645ea5f87253b3506a8be42674e982bd Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 09:56:45 -0700 Subject: [PATCH 13/14] fix: mount docker socket at /run not /var/run (symlink issue) /var/run is a symlink to /run in Alpine. Mounting emptyDir at /var/run doesn't override the real /run directory, so dind's docker.sock at /run/docker.sock was never visible to the runner container. Fix: Mount the shared emptyDir at /run in both containers. --- k8s/infra/forgejo-runner/templates/deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k8s/infra/forgejo-runner/templates/deployment.yaml b/k8s/infra/forgejo-runner/templates/deployment.yaml index 5412b78..f60ab36 100644 --- a/k8s/infra/forgejo-runner/templates/deployment.yaml +++ b/k8s/infra/forgejo-runner/templates/deployment.yaml @@ -71,7 +71,7 @@ spec: - name: docker-certs mountPath: /docker-certs - name: docker-sock - mountPath: /var/run + mountPath: /run - name: homelab-ca mountPath: /etc/ssl/certs/homelab-ca.pem subPath: ca.crt @@ -92,7 +92,7 @@ spec: - name: docker-certs mountPath: /docker-certs - name: docker-sock - mountPath: /var/run + mountPath: /run - name: dind-storage mountPath: /var/lib/docker - name: homelab-ca -- 2.54.0 From 8b83350cb17bff605302b938f1c39eebc4fc39e3 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 21:47:07 -0700 Subject: [PATCH 14/14] chore: remove custom runner Dockerfiles and build workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No longer needed — runner labels now point to official language images (golang:1.26-bookworm, node:22-bookworm, rust:1-bookworm) which already have the language tools. Docker CLI installed via apt-get in workflow steps. --- .gitea/workflows/build-runner-images.yml | 65 ---------------------- k8s/infra/forgejo-runner/Dockerfile.golang | 16 ------ k8s/infra/forgejo-runner/Dockerfile.node | 16 ------ k8s/infra/forgejo-runner/Dockerfile.rust | 20 ------- 4 files changed, 117 deletions(-) delete mode 100644 .gitea/workflows/build-runner-images.yml delete mode 100644 k8s/infra/forgejo-runner/Dockerfile.golang delete mode 100644 k8s/infra/forgejo-runner/Dockerfile.node delete mode 100644 k8s/infra/forgejo-runner/Dockerfile.rust diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml deleted file mode 100644 index 12d1621..0000000 --- a/.gitea/workflows/build-runner-images.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Build and push runner images - -on: - push: - paths: - - 'k8s/infra/forgejo-runner/Dockerfile.golang' - - 'k8s/infra/forgejo-runner/Dockerfile.rust' - - 'k8s/infra/forgejo-runner/Dockerfile.node' - branches: - - main - pull_request: - paths: - - 'k8s/infra/forgejo-runner/Dockerfile.golang' - - 'k8s/infra/forgejo-runner/Dockerfile.rust' - - 'k8s/infra/forgejo-runner/Dockerfile.node' - - '.gitea/workflows/build-runner-images.yml' - -jobs: - build-runners: - runs-on: golang - # Unified CI pattern: golang label → golang:1.26-bookworm (Debian, root, apt-get) - # Install Node.js + docker in steps. actions/checkout@v4 needs Node.js. - steps: - - name: Install dependencies - run: | - apt-get update - apt-get install -y --no-install-recommends nodejs docker.io - rm -rf /var/lib/apt/lists/* - - - uses: actions/checkout@v4 - - - name: Get commit SHA - id: sha - run: | - SHORT_SHA=$(git rev-parse --short HEAD) - echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - - - name: Build all runner images - run: | - set -e - for RUNNER in golang rust node; do - echo "šŸ“¦ Building ${RUNNER}-runner..." - docker build -f "k8s/infra/forgejo-runner/Dockerfile.${RUNNER}" \ - -t "${IMAGE_BASE}/forgejo-runner-${RUNNER}:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE_BASE}/forgejo-runner-${RUNNER}:latest" \ - . - echo "āœ… Built ${RUNNER}-runner" - done - - - name: Push images (main only) - if: github.event_name == 'push' - run: | - echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ - --username "${REGISTRY_USER}" --password-stdin - for RUNNER in golang rust node; do - docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE_BASE}/forgejo-runner-${RUNNER}:latest" - echo "āœ… Pushed ${RUNNER}-runner" - done - env: - REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} - REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} - env: - REGISTRY: forgejo.riotpiao.com - IMAGE_BASE: forgejo.riotpiao.com/rock diff --git a/k8s/infra/forgejo-runner/Dockerfile.golang b/k8s/infra/forgejo-runner/Dockerfile.golang deleted file mode 100644 index 0a994c6..0000000 --- a/k8s/infra/forgejo-runner/Dockerfile.golang +++ /dev/null @@ -1,16 +0,0 @@ -FROM code.forgejo.org/forgejo/runner:6 - -# Switch to root to install packages (Alpine) -USER root - -# Alpine uses apk, not apt-get -RUN apk update && apk add --no-cache \ - nodejs \ - npm \ - docker - -# Verify installations -RUN docker --version && node --version && git --version - -# Switch back to runner user -USER 1000:1000 diff --git a/k8s/infra/forgejo-runner/Dockerfile.node b/k8s/infra/forgejo-runner/Dockerfile.node deleted file mode 100644 index 580f024..0000000 --- a/k8s/infra/forgejo-runner/Dockerfile.node +++ /dev/null @@ -1,16 +0,0 @@ -FROM code.forgejo.org/forgejo/runner:6 - -# Switch to root to install packages (Alpine) -USER root - -# Alpine uses apk, not apt-get -RUN apk update && apk add --no-cache \ - docker \ - nodejs \ - npm - -# Verify installations -RUN node --version && docker --version && git --version - -# Switch back to runner user -USER 1000:1000 diff --git a/k8s/infra/forgejo-runner/Dockerfile.rust b/k8s/infra/forgejo-runner/Dockerfile.rust deleted file mode 100644 index 51eab38..0000000 --- a/k8s/infra/forgejo-runner/Dockerfile.rust +++ /dev/null @@ -1,20 +0,0 @@ -FROM code.forgejo.org/forgejo/runner:6 - -# Switch to root to install packages (Alpine) -USER root - -# Alpine uses apk, not apt-get -RUN apk update && apk add --no-cache \ - nodejs \ - npm \ - curl \ - docker - -# Install Rust (as root, skip verification for now) -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable || true - -# Verify core installations -RUN docker --version && node --version && git --version - -# Switch back to runner user -USER 1000:1000 -- 2.54.0