From 9616e0e2b352944746b401232eaa7135e3ecb02a Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:16:59 +1000 Subject: [PATCH 01/13] arrstack: use canonical upstream image names (#427) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the arrproxy migrate job at `docker.io/library/postgres:18-alpine`. - Point the arrproxy oauth2-proxy cert-combine init container at `docker.io/library/alpine:3`. - Point the arrstack ValkeyCluster at `docker.io/valkey/valkey:9.0.0`. Tags are unchanged. `kustomize build --enable-helm apps/overlays/au-syd1/arrstack` differs from main only in those three image strings. No extra proxied refs found in these files (the oauth2-proxy image itself is already canonical `quay.io/...`). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/427 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/arrstack/arrproxy/migrate-job.yaml | 2 +- apps/base/arrstack/arrproxy/oauth2-proxy-deployment.yaml | 2 +- apps/base/arrstack/valkey/valkeycluster.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/base/arrstack/arrproxy/migrate-job.yaml b/apps/base/arrstack/arrproxy/migrate-job.yaml index 7f908f6..6cc9fe5 100644 --- a/apps/base/arrstack/arrproxy/migrate-job.yaml +++ b/apps/base/arrstack/arrproxy/migrate-job.yaml @@ -35,7 +35,7 @@ spec: type: RuntimeDefault containers: - name: migrate - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/postgres:18-alpine + image: docker.io/library/postgres:18-alpine imagePullPolicy: IfNotPresent env: - name: HOME diff --git a/apps/base/arrstack/arrproxy/oauth2-proxy-deployment.yaml b/apps/base/arrstack/arrproxy/oauth2-proxy-deployment.yaml index 4b5faa4..70fb45e 100644 --- a/apps/base/arrstack/arrproxy/oauth2-proxy-deployment.yaml +++ b/apps/base/arrstack/arrproxy/oauth2-proxy-deployment.yaml @@ -35,7 +35,7 @@ spec: # identity.unkin.net serves a Vault-PKI cert; combine the system roots # with the internal CA so oauth2-proxy's OIDC HTTP client trusts it. - name: combine-certs - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/alpine:3 + image: docker.io/library/alpine:3 imagePullPolicy: IfNotPresent command: - sh diff --git a/apps/base/arrstack/valkey/valkeycluster.yaml b/apps/base/arrstack/valkey/valkeycluster.yaml index 2c107df..87d207f 100644 --- a/apps/base/arrstack/valkey/valkeycluster.yaml +++ b/apps/base/arrstack/valkey/valkeycluster.yaml @@ -28,7 +28,7 @@ metadata: spec: shards: 1 replicas: 2 - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/valkey/valkey:9.0.0 + image: docker.io/valkey/valkey:9.0.0 exporter: enabled: false scheduling: From e10f0c09c9a2d4adf79f88468fb1914cc37a13b6 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:18:09 +1000 Subject: [PATCH 02/13] arrstack,fafflix,cheeztv: provision shared mediastore CephFS volume (#428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Why The media estate is currently split across two 1Ti CephFS subvolumes (`media-tv` and `media-movies`), so a download that lands in one and imports to the other is a cross-filesystem copy, not a hardlink move. A single 10Ti `mediastore` subvolume (already created on `cephfs_csi_ssd_ec_4_1`) collapses that: one filesystem holding `/{fafflix,cheeztv}/{tvseries,movies}` plus `/nzbget/downloads/complete`, so every arr import is an atomic same-fs hardlink. This PR only provisions the volume and seeds its tree. No existing PV, PVC or workload is touched, and nothing mounts the new claims yet — the cutover of the arrs and the two jellyfins is a separate change. ## How - Add static PVs `arrstack-mediastore`, `fafflix-mediastore` and `cheeztv-mediastore`, all pointing at the same rootPath (`/volumes/csi_ssd_ec_4_1/mediastore/a0152dac-…`) with unique names and volumeHandles pinned by `claimRef` — the established pattern for the shared media subvolumes. - Add the matching RWX 10Ti PVCs (`mediastore` in arrstack, `fafflix-mediastore`, `cheeztv-mediastore`), annotated `k8up.io/backup: "false"` and statically bound via `volumeName` + `storageClassName: ""`. - Add `mediastore-bootstrap`, a one-shot ArgoCD Sync-hook Job (`hook-delete-policy: BeforeHookCreation`, no sync-wave needed) that mounts the arrstack claim and `mkdir -p`s the directory tree as uid/gid 1000 — the uid the arrstack media pods run as. Idempotent, so it self-heals on every sync. - Wire the new manifests into the arrstack, fafflix and cheeztv bases. ## Validation - `kustomize build` clean on `apps/overlays/au-syd1/{arrstack,fafflix,cheeztv}` - `kubeconform` clean on all three overlays (91 / 29 / 32 resources valid) - pre-commit (yamllint, check-yaml, no-plain-secrets) passed Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/428 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/arrstack/kustomization.yaml | 3 + .../arrstack/mediastore-bootstrap-job.yaml | 74 +++++++++++++++++++ apps/base/arrstack/pv-mediastore.yaml | 32 ++++++++ apps/base/arrstack/pvc-mediastore.yaml | 22 ++++++ apps/base/cheeztv/kustomization.yaml | 2 + apps/base/cheeztv/pv-mediastore.yaml | 31 ++++++++ apps/base/cheeztv/pvc-mediastore.yaml | 24 ++++++ apps/base/fafflix/kustomization.yaml | 2 + apps/base/fafflix/pv-mediastore.yaml | 31 ++++++++ apps/base/fafflix/pvc-mediastore.yaml | 24 ++++++ 10 files changed, 245 insertions(+) create mode 100644 apps/base/arrstack/mediastore-bootstrap-job.yaml create mode 100644 apps/base/arrstack/pv-mediastore.yaml create mode 100644 apps/base/arrstack/pvc-mediastore.yaml create mode 100644 apps/base/cheeztv/pv-mediastore.yaml create mode 100644 apps/base/cheeztv/pvc-mediastore.yaml create mode 100644 apps/base/fafflix/pv-mediastore.yaml create mode 100644 apps/base/fafflix/pvc-mediastore.yaml diff --git a/apps/base/arrstack/kustomization.yaml b/apps/base/arrstack/kustomization.yaml index f975b7d..1ddf27f 100644 --- a/apps/base/arrstack/kustomization.yaml +++ b/apps/base/arrstack/kustomization.yaml @@ -8,9 +8,12 @@ resources: - pv-media-tv.yaml - pv-media-movies.yaml - pv-mediafs.yaml + - pv-mediastore.yaml - pvc-media-tv.yaml - pvc-media-movies.yaml - pvc-mediafs.yaml + - pvc-mediastore.yaml + - mediastore-bootstrap-job.yaml - media-bucket.yaml - backups-bucket.yaml - postgres diff --git a/apps/base/arrstack/mediastore-bootstrap-job.yaml b/apps/base/arrstack/mediastore-bootstrap-job.yaml new file mode 100644 index 0000000..846b070 --- /dev/null +++ b/apps/base/arrstack/mediastore-bootstrap-job.yaml @@ -0,0 +1,74 @@ +--- +# Seeds the directory skeleton on the freshly created mediastore subvolume so +# the arrs, nzbget and both jellyfins mount subPaths that already exist and are +# owned by uid/gid 1000 (the uid every arrstack media pod runs as). mkdir -p is +# idempotent, so re-running it on every sync is harmless and self-heals a tree +# someone deleted by hand. +# +# Sync hook with BeforeHookCreation delete: ArgoCD replaces the completed Job +# each sync instead of failing on the immutable pod template. No sync-wave is +# needed -- the PVC applies in the same wave and the pod simply stays Pending +# until it binds. +apiVersion: batch/v1 +kind: Job +metadata: + name: mediastore-bootstrap + namespace: arrstack + annotations: + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation +spec: + backoffLimit: 6 + ttlSecondsAfterFinished: 600 + template: + metadata: + labels: + app: mediastore-bootstrap + spec: + serviceAccountName: default + automountServiceAccountToken: false + restartPolicy: Never + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + fsGroupChangePolicy: OnRootMismatch + seccompProfile: + type: RuntimeDefault + containers: + - name: mkdir + image: docker.io/library/alpine:3 + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - | + set -eu + mkdir -p \ + /media/fafflix/tvseries \ + /media/fafflix/movies \ + /media/cheeztv/tvseries \ + /media/cheeztv/movies \ + /media/nzbget/downloads/complete + ls -la /media + volumeMounts: + - name: mediastore + mountPath: /media + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 200m + memory: 128Mi + volumes: + - name: mediastore + persistentVolumeClaim: + claimName: mediastore diff --git a/apps/base/arrstack/pv-mediastore.yaml b/apps/base/arrstack/pv-mediastore.yaml new file mode 100644 index 0000000..cad3614 --- /dev/null +++ b/apps/base/arrstack/pv-mediastore.yaml @@ -0,0 +1,32 @@ +--- +# Static PV for the shared MEDIASTORE CephFS subvolume: one 10Ti filesystem +# holding every library plus the nzbget download tree, so arr imports are +# same-filesystem hardlink moves across tv AND movies. Same rootPath as the +# fafflix/cheeztv mediastore PVs; each namespace gets its own PV (unique name + +# volumeHandle) pinned by claimRef. +apiVersion: v1 +kind: PersistentVolume +metadata: + name: arrstack-mediastore +spec: + capacity: + storage: 10Ti + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: "" + volumeMode: Filesystem + claimRef: + namespace: arrstack + name: mediastore + csi: + driver: cephfs.csi.ceph.com + volumeHandle: arrstack-mediastore-static + nodeStageSecretRef: + name: csi-cephfs-secret + namespace: csi-cephfs + volumeAttributes: + staticVolume: "true" + clusterID: cephfs_csi_ssd_ec_4_1 + fsName: cephfs + rootPath: /volumes/csi_ssd_ec_4_1/mediastore/a0152dac-a51b-4b95-ac5e-ecdd99bfe3f1 diff --git a/apps/base/arrstack/pvc-mediastore.yaml b/apps/base/arrstack/pvc-mediastore.yaml new file mode 100644 index 0000000..b6a4ebd --- /dev/null +++ b/apps/base/arrstack/pvc-mediastore.yaml @@ -0,0 +1,22 @@ +--- +# Whole media tree (/fafflix, /cheeztv, /nzbget) on one RWX filesystem, shared +# across the sonarr/radarr/nzbget pods. Statically bound to the +# arrstack-mediastore PV (the same CephFS subvolume fafflix and cheeztv mount). +# storageClassName "" + volumeName disables dynamic provisioning and binds the +# pre-created static PV. +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mediastore + namespace: arrstack + annotations: + k8up.io/backup: "false" +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 10Ti + storageClassName: "" + volumeName: arrstack-mediastore + volumeMode: Filesystem diff --git a/apps/base/cheeztv/kustomization.yaml b/apps/base/cheeztv/kustomization.yaml index d2454f3..3c953ff 100644 --- a/apps/base/cheeztv/kustomization.yaml +++ b/apps/base/cheeztv/kustomization.yaml @@ -15,8 +15,10 @@ resources: - pvc-transcode.yaml - pv-media-tv.yaml - pv-media-movies.yaml + - pv-mediastore.yaml - pvc-media-tv.yaml - pvc-media-movies.yaml + - pvc-mediastore.yaml - statefulset.yaml - plugin-configmap.yaml - pdb.yaml diff --git a/apps/base/cheeztv/pv-mediastore.yaml b/apps/base/cheeztv/pv-mediastore.yaml new file mode 100644 index 0000000..91ae2a5 --- /dev/null +++ b/apps/base/cheeztv/pv-mediastore.yaml @@ -0,0 +1,31 @@ +--- +# Static PV for the shared MEDIASTORE CephFS subvolume. Same rootPath as +# arrstack's mediastore PV so the arrs write and cheeztv reads the identical +# library tree (cheeztv scans /cheeztv/{tvseries,movies}); each namespace gets +# its own PV (unique name + volumeHandle) pinned by claimRef. +apiVersion: v1 +kind: PersistentVolume +metadata: + name: cheeztv-mediastore +spec: + capacity: + storage: 10Ti + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: "" + volumeMode: Filesystem + claimRef: + namespace: cheeztv + name: cheeztv-mediastore + csi: + driver: cephfs.csi.ceph.com + volumeHandle: cheeztv-mediastore-static + nodeStageSecretRef: + name: csi-cephfs-secret + namespace: csi-cephfs + volumeAttributes: + staticVolume: "true" + clusterID: cephfs_csi_ssd_ec_4_1 + fsName: cephfs + rootPath: /volumes/csi_ssd_ec_4_1/mediastore/a0152dac-a51b-4b95-ac5e-ecdd99bfe3f1 diff --git a/apps/base/cheeztv/pvc-mediastore.yaml b/apps/base/cheeztv/pvc-mediastore.yaml new file mode 100644 index 0000000..95e6fe2 --- /dev/null +++ b/apps/base/cheeztv/pvc-mediastore.yaml @@ -0,0 +1,24 @@ +--- +# Shared media tree, read-many across replicas. Statically bound to the +# cheeztv-mediastore PV (the CephFS subvolume also used by arrstack and +# fafflix). storageClassName "" + volumeName disables dynamic provisioning and +# binds the pre-created static PV. +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: cheeztv-mediastore + namespace: cheeztv + annotations: + # Exclude from the cheeztv-config k8up Schedule (skipWithoutAnnotation is + # false cluster-wide, so unannotated PVCs are swept in). Only cheeztv-config + # is backed up; the media library is not restic-backup material. + k8up.io/backup: "false" +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 10Ti + storageClassName: "" + volumeName: cheeztv-mediastore + volumeMode: Filesystem diff --git a/apps/base/fafflix/kustomization.yaml b/apps/base/fafflix/kustomization.yaml index d2454f3..3c953ff 100644 --- a/apps/base/fafflix/kustomization.yaml +++ b/apps/base/fafflix/kustomization.yaml @@ -15,8 +15,10 @@ resources: - pvc-transcode.yaml - pv-media-tv.yaml - pv-media-movies.yaml + - pv-mediastore.yaml - pvc-media-tv.yaml - pvc-media-movies.yaml + - pvc-mediastore.yaml - statefulset.yaml - plugin-configmap.yaml - pdb.yaml diff --git a/apps/base/fafflix/pv-mediastore.yaml b/apps/base/fafflix/pv-mediastore.yaml new file mode 100644 index 0000000..717c2ca --- /dev/null +++ b/apps/base/fafflix/pv-mediastore.yaml @@ -0,0 +1,31 @@ +--- +# Static PV for the shared MEDIASTORE CephFS subvolume. Same rootPath as +# arrstack's mediastore PV so the arrs write and fafflix reads the identical +# library tree (fafflix scans /fafflix/{tvseries,movies}); each namespace gets +# its own PV (unique name + volumeHandle) pinned by claimRef. +apiVersion: v1 +kind: PersistentVolume +metadata: + name: fafflix-mediastore +spec: + capacity: + storage: 10Ti + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: "" + volumeMode: Filesystem + claimRef: + namespace: fafflix + name: fafflix-mediastore + csi: + driver: cephfs.csi.ceph.com + volumeHandle: fafflix-mediastore-static + nodeStageSecretRef: + name: csi-cephfs-secret + namespace: csi-cephfs + volumeAttributes: + staticVolume: "true" + clusterID: cephfs_csi_ssd_ec_4_1 + fsName: cephfs + rootPath: /volumes/csi_ssd_ec_4_1/mediastore/a0152dac-a51b-4b95-ac5e-ecdd99bfe3f1 diff --git a/apps/base/fafflix/pvc-mediastore.yaml b/apps/base/fafflix/pvc-mediastore.yaml new file mode 100644 index 0000000..ceb0011 --- /dev/null +++ b/apps/base/fafflix/pvc-mediastore.yaml @@ -0,0 +1,24 @@ +--- +# Shared media tree, read-many across replicas. Statically bound to the +# fafflix-mediastore PV (the CephFS subvolume also used by arrstack and +# cheeztv). storageClassName "" + volumeName disables dynamic provisioning and +# binds the pre-created static PV. +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: fafflix-mediastore + namespace: fafflix + annotations: + # Exclude from the fafflix-config k8up Schedule (skipWithoutAnnotation is + # false cluster-wide, so unannotated PVCs are swept in). Only fafflix-config + # is backed up; the media library is not restic-backup material. + k8up.io/backup: "false" +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 10Ti + storageClassName: "" + volumeName: fafflix-mediastore + volumeMode: Filesystem From c8355967d4e9dd3e141860ca8a4f2d9ccf187137 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:18:27 +1000 Subject: [PATCH 03/13] artifactapi: use canonical upstream image name for redis_exporter (#429) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the artifactapi redis metrics sidecar at `docker.io/oliver006/redis_exporter:v1.89.0`. Tag unchanged. `kustomize build --enable-helm apps/overlays/au-syd1/artifactapi` differs from main only in that image string. No extra proxied refs in the file (the `redis:7-alpine` container is already a bare upstream name). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/429 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/artifactapi/redis-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/base/artifactapi/redis-deployment.yaml b/apps/base/artifactapi/redis-deployment.yaml index 4298e18..19f6e44 100644 --- a/apps/base/artifactapi/redis-deployment.yaml +++ b/apps/base/artifactapi/redis-deployment.yaml @@ -54,7 +54,7 @@ spec: successThreshold: 1 timeoutSeconds: 5 - name: metrics-exporter - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/oliver006/redis_exporter:v1.89.0 + image: docker.io/oliver006/redis_exporter:v1.89.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9121 From b459e9a90a40be0b9d9f748a2b805a350d5ce95e Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:18:33 +1000 Subject: [PATCH 04/13] authentik: use canonical upstream image name for redis_exporter (#430) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the authentik redis metrics sidecar at `docker.io/oliver006/redis_exporter:v1.89.0`. Tag unchanged. `kustomize build --enable-helm apps/overlays/au-syd1/authentik` differs from main only in that image string. No extra proxied refs in the file (the `redis:7-alpine` container is already a bare upstream name). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/430 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/authentik/redis-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/base/authentik/redis-deployment.yaml b/apps/base/authentik/redis-deployment.yaml index 5cc8552..c14838f 100644 --- a/apps/base/authentik/redis-deployment.yaml +++ b/apps/base/authentik/redis-deployment.yaml @@ -53,7 +53,7 @@ spec: - mountPath: /data name: redis-data - name: metrics-exporter - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/oliver006/redis_exporter:v1.89.0 + image: docker.io/oliver006/redis_exporter:v1.89.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9121 From 7f928dddfce0f956bd4d57086a65119840fab11a Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:18:44 +1000 Subject: [PATCH 05/13] gitea: use canonical upstream image name for redis_exporter (#431) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the gitea valkey metrics sidecar at `docker.io/oliver006/redis_exporter:v1.89.0`. Tag unchanged. `kustomize build --enable-helm apps/overlays/au-syd1/gitea` differs from main only in that image string. No extra proxied refs in the file (the `valkey/valkey:8-alpine` container is already a bare upstream name). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/431 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/gitea/valkey-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/base/gitea/valkey-deployment.yaml b/apps/base/gitea/valkey-deployment.yaml index 3015036..00163f7 100644 --- a/apps/base/gitea/valkey-deployment.yaml +++ b/apps/base/gitea/valkey-deployment.yaml @@ -83,7 +83,7 @@ spec: - mountPath: /data name: data - name: metrics-exporter - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/oliver006/redis_exporter:v1.89.0 + image: docker.io/oliver006/redis_exporter:v1.89.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9121 From df89947f4746cf09ff8d6910f6837f9e1d476c7c Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:19:01 +1000 Subject: [PATCH 06/13] litellm: use canonical upstream image name for redis_exporter (#432) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the litellm redis metrics sidecar at `docker.io/oliver006/redis_exporter:v1.89.0`. Tag unchanged. `kustomize build --enable-helm apps/overlays/au-syd1/litellm` differs from main only in that image string. No extra proxied refs in the file (the `redis:7-alpine` container is already a bare upstream name). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/432 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/litellm/redis-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/base/litellm/redis-deployment.yaml b/apps/base/litellm/redis-deployment.yaml index d47597f..a5101c5 100644 --- a/apps/base/litellm/redis-deployment.yaml +++ b/apps/base/litellm/redis-deployment.yaml @@ -61,7 +61,7 @@ spec: mountPropagation: None name: data - name: metrics-exporter - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/oliver006/redis_exporter:v1.89.0 + image: docker.io/oliver006/redis_exporter:v1.89.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9121 From da1d812eec8c08413978f2f7be9fda383a49c199 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:19:27 +1000 Subject: [PATCH 07/13] netbox: use canonical upstream image name for redis_exporter (#434) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the netbox valkey metrics sidecar at `docker.io/oliver006/redis_exporter:v1.89.0`. Tag unchanged. `kustomize build --enable-helm apps/overlays/au-syd1/netbox` differs from main only in that image string. No extra proxied refs in the file (the `valkey/valkey:8-alpine` container is already a bare upstream name). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/434 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/netbox/valkey-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/base/netbox/valkey-deployment.yaml b/apps/base/netbox/valkey-deployment.yaml index 9070964..a122b13 100644 --- a/apps/base/netbox/valkey-deployment.yaml +++ b/apps/base/netbox/valkey-deployment.yaml @@ -83,7 +83,7 @@ spec: - mountPath: /data name: data - name: metrics-exporter - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/oliver006/redis_exporter:v1.89.0 + image: docker.io/oliver006/redis_exporter:v1.89.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9121 From 38a41bd44b807738805e8ce057793a33b9a3b4aa Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:19:36 +1000 Subject: [PATCH 08/13] watchstate: use canonical upstream image name for alpine (#435) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the watchstate oauth2-proxy cert-combine init container at `docker.io/library/alpine:3`. Tag unchanged. `kustomize build --enable-helm apps/overlays/au-syd1/watchstate` differs from main only in that image string. No extra proxied refs in the file (the oauth2-proxy image itself is already canonical `quay.io/...`). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/435 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/watchstate/oauth2-proxy-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/base/watchstate/oauth2-proxy-deployment.yaml b/apps/base/watchstate/oauth2-proxy-deployment.yaml index 6c1d3ea..a4cca26 100644 --- a/apps/base/watchstate/oauth2-proxy-deployment.yaml +++ b/apps/base/watchstate/oauth2-proxy-deployment.yaml @@ -35,7 +35,7 @@ spec: # system roots with the internal CA so oauth2-proxy's OIDC HTTP client # trusts it. - name: combine-certs - image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/alpine:3 + image: docker.io/library/alpine:3 imagePullPolicy: IfNotPresent command: - sh From 6b5b129ad6084c0da62be282217b2ed94ac20b95 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:20:10 +1000 Subject: [PATCH 09/13] clickhouse-system: use canonical upstream image names (#436) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Point the Altinity chart crdHook at `docker.io/bitnami/kubectl`. - Point the operator at `docker.io/altinity/clickhouse-operator`. - Point the metrics exporter at `docker.io/altinity/metrics-exporter`. - Reword the header comment that claimed all images are pulled through the artifactapi dockerhub remote. Only the `repository` keys change; the chart still supplies the tags (0.27.2 / latest), so rendered tags are identical. `kustomize build --enable-helm apps/overlays/au-syd1/clickhouse-system` differs from main only in those three image strings. No other proxied refs in the file. Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/436 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/overlays/au-syd1/clickhouse-system/values.yaml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/overlays/au-syd1/clickhouse-system/values.yaml b/apps/overlays/au-syd1/clickhouse-system/values.yaml index 52b09b9..74b21a9 100644 --- a/apps/overlays/au-syd1/clickhouse-system/values.yaml +++ b/apps/overlays/au-syd1/clickhouse-system/values.yaml @@ -2,10 +2,9 @@ # resources in all namespaces (the logs cluster lives in the `logging` namespace). # CRDs are installed at runtime by the chart's crdHook Job. # -# All images are pulled through the artifactapi dockerhub remote (no direct -# upstream). Upstream official images are used; no Docker Hardened Image variant -# is adopted (DHI is subscription-gated and served from a private org namespace -# not reachable via the anonymous artifactapi dockerhub proxy). +# Upstream official images; no Docker Hardened Image variant is adopted (DHI is +# subscription-gated and served from a private org namespace not reachable via +# the anonymous artifactapi dockerhub proxy). # # Watch the logging namespace where the ClickHouseInstallation lives. The chart # default (watchNamespaces: []) makes the operator watch ONLY its own namespace @@ -14,7 +13,7 @@ watchNamespaces: - logging crdHook: image: - repository: artifactapi.k8s.syd1.au.unkin.net/dockerhub/bitnami/kubectl + repository: docker.io/bitnami/kubectl resources: requests: cpu: 50m @@ -25,7 +24,7 @@ crdHook: operator: image: - repository: artifactapi.k8s.syd1.au.unkin.net/dockerhub/altinity/clickhouse-operator + repository: docker.io/altinity/clickhouse-operator resources: requests: cpu: 100m @@ -36,7 +35,7 @@ operator: metrics: image: - repository: artifactapi.k8s.syd1.au.unkin.net/dockerhub/altinity/metrics-exporter + repository: docker.io/altinity/metrics-exporter resources: requests: cpu: 50m From 74ad2c8773204a6c4735e294bb1008e02b650323 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:21:21 +1000 Subject: [PATCH 10/13] woodpecker: add mediamark-ci service account (#438) The mediamark Woodpecker docker step needs a dedicated ServiceAccount so it can push to the trusted in-cluster registry, mirroring the existing arrproxy-ci setup. - Add bare ServiceAccount `mediamark-ci` in namespace `woodpecker` and register it in the woodpecker base kustomization. Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/438 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/woodpecker/kustomization.yaml | 1 + apps/base/woodpecker/serviceaccount_mediamark_ci.yaml | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 apps/base/woodpecker/serviceaccount_mediamark_ci.yaml diff --git a/apps/base/woodpecker/kustomization.yaml b/apps/base/woodpecker/kustomization.yaml index d3c9360..6e0df22 100644 --- a/apps/base/woodpecker/kustomization.yaml +++ b/apps/base/woodpecker/kustomization.yaml @@ -11,6 +11,7 @@ resources: - serviceaccount_autobackup_operator_ci.yaml - serviceaccount_ghp.yaml - serviceaccount_kea_operator_ci.yaml + - serviceaccount_mediamark_ci.yaml - serviceaccount_plugin_docker_buildx.yaml - serviceaccount_jellyfin_ha_src.yaml - serviceaccount_terraform_artifactapi.yaml diff --git a/apps/base/woodpecker/serviceaccount_mediamark_ci.yaml b/apps/base/woodpecker/serviceaccount_mediamark_ci.yaml new file mode 100644 index 0000000..920b535 --- /dev/null +++ b/apps/base/woodpecker/serviceaccount_mediamark_ci.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mediamark-ci + namespace: woodpecker From aedb721b3ee471a9f50ccd2973820d938b3247b7 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:52:12 +1000 Subject: [PATCH 11/13] argocd: drop internal-CA rootCA pin from Authentik oidc.config (#439) ## Why `identity.unkin.net` moved from an internal `unkin.net` CA-issued cert to the LetsEncrypt `*.unkin.net` wildcard. The `oidc.config` block pinned the internal root as the *only* trust anchor, so argocd-server now rejects OIDC discovery with `x509: certificate signed by unknown authority` and SSO login is broken. The stock image trust store already carries the public roots. ## Changes - Remove the `rootCA:` block from `argocd-cm`'s `oidc.config` for the `https://identity.unkin.net/application/o/argocd/` issuer. - Replace the now-false internal-CA rationale comment with a one-liner noting the LE-issued cert needs no pin. - Leave issuer, clientID, clientSecret ref, `requestedScopes` (incl. `ak_groups`) and `requestedIDTokenClaims` untouched. Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/439 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- .../au-syd1/bootstrap/argocd-cm-patch.yaml | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/clusters/au-syd1/bootstrap/argocd-cm-patch.yaml b/clusters/au-syd1/bootstrap/argocd-cm-patch.yaml index 8701640..fe2f258 100644 --- a/clusters/au-syd1/bootstrap/argocd-cm-patch.yaml +++ b/clusters/au-syd1/bootstrap/argocd-cm-patch.yaml @@ -26,33 +26,8 @@ data: issuer: https://identity.unkin.net/application/o/argocd/ clientID: argocd clientSecret: $argocd-oidc:client_secret - # argocd-server does OIDC discovery/egress to identity.unkin.net over TLS, - # which is served by the internal `unkin.net` CA (not a public root), so the - # stock image trust store rejects it (x509: certificate signed by unknown - # authority). Anchor on the stable `unkin.net` root; identity presents its - # intermediate in the handshake, and the intermediate is periodically - # re-keyed, so pinning the root (not the intermediate) is rotation-proof. - rootCA: | - -----BEGIN CERTIFICATE----- - MIIDLzCCAhegAwIBAgIUIDADwsHIrQ8dfncpechBdIUCQdIwDQYJKoZIhvcNAQEL - BQAwFDESMBAGA1UEAxMJdW5raW4ubmV0MB4XDTI0MDQyNzExMjcwMloXDTM0MDQy - NTExMjczMlowFDESMBAGA1UEAxMJdW5raW4ubmV0MIIBIjANBgkqhkiG9w0BAQEF - AAOCAQ8AMIIBCgKCAQEA3ENPv7R7gCUJAg8Q4hB2LEZSdvbK155YbcrguLDDnu6m - 2fkJn8jYMMW3Z6/+Y04ouGwi6sKup8ggTb217sY+dC4IUZjotDPAhruxfXVQAh0v - Yr3RYoxVDrm4nRSFLo1RA4Qt+1KK299mHGQf9iAiwbsFp5mDrJT9uz15FE2uWmbK - 8/onMyJC4fnkMihVN6NIgTtjpHYNm5aAJwxoWldTopgF0ucb7X3XVPNbKAmd3Avd - lsOo6m751zSZ0HvJOxgRSy7lvPzMuUfCQsOcmI4O4+Z2FL4Y7p+T9DvWkciC7L3i - tBiK30fPfGKNpWaof1ONCcPQNjMwWcEFXqSiWUOXkwIDAQABo3kwdzAOBgNVHQ8B - Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGpy/pj6F8e3gSOAp - r+6hAYQdOScwHwYDVR0jBBgwFoAUGpy/pj6F8e3gSOApr+6hAYQdOScwFAYDVR0R - BA0wC4IJdW5raW4ubmV0MA0GCSqGSIb3DQEBCwUAA4IBAQA5xocILzuvD+R2Iub1 - UnTdcVpgNcxJmESz0eX4UrkcBmddtuFINXvDTv5//XTFs78LsVVSf00xZ+2C62Xe - xRdCdluHN8VDCAKulP4XJY1BiZ7im0v+iMgPDKhq4OXb86WFYI/8J6uRm7oIAwj1 - zhhKxMimkzli+yHB8ipL15W7l68CMUgmOjFA+EG6sbfadFpQTX/h6TVj3FQPkU/p - UJEm2XjlGNAKGJrNRU47PM4vRDv5Joyowp9zv/pHFXvUJladaJupMKRJQVWQz1US - EXE67rawG79s3vm8dDolnbli/IhPHtjDRIprxAwrMs5tt9cY0xsRkFBZVcAOjrpb - 4gqd - -----END CERTIFICATE----- + # identity.unkin.net now serves the LetsEncrypt *.unkin.net wildcard, so the + # stock image trust store validates it; no rootCA pin. requestedScopes: - openid - profile From 1169d796e7f3b0b6261e14586cb9f999b3a0db2b Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:52:30 +1000 Subject: [PATCH 12/13] grafana: stop pinning the internal CA for Authentik OAuth (#440) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Why `identity.unkin.net` moved from an internal `unkin.net` CA-issued cert to the LetsEncrypt `*.unkin.net` wildcard. `auth.generic_oauth`'s `tls_client_ca` pointed Grafana at the internal root only, so the OAuth handshake to the LE-issued cert now fails. Grafana's image trust store already contains the public roots. ## Changes - Remove `tls_client_ca: /etc/grafana/vault-ca/ca.crt` (and its stale comment) from `auth.generic_oauth`. - Remove the now-unused `vault-ca-cert` volume and volumeMount from the Grafana pod spec — nothing else in the pod referenced it (the CNPG `endpointCA` reference to `vault-ca-cert` for `s3.ceph.unkin.net` is a separate resource and stays). - Leave the auth/token/api URLs, scopes and `role_attribute_path` untouched. Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/440 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/base/grafana/grafana.yaml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/apps/base/grafana/grafana.yaml b/apps/base/grafana/grafana.yaml index 82b6a1f..1665d71 100644 --- a/apps/base/grafana/grafana.yaml +++ b/apps/base/grafana/grafana.yaml @@ -26,13 +26,6 @@ spec: secretKeyRef: name: oauth-credentials key: client_secret - # identity.unkin.net is served by the internal unkin.net CA, which - # the stock Grafana image doesn't trust. Mount the reflected - # vault-ca-cert and point generic_oauth's tls_client_ca at it. - volumeMounts: - - name: vault-ca-cert - mountPath: /etc/grafana/vault-ca - readOnly: true resources: requests: cpu: 100m @@ -40,13 +33,6 @@ spec: limits: cpu: "1" memory: 1Gi - volumes: - - name: vault-ca-cert - secret: - secretName: vault-ca-cert - items: - - key: ca.crt - path: ca.crt config: server: root_url: "https://grafana.k8s.syd1.au.unkin.net" @@ -71,9 +57,6 @@ spec: auth_url: "https://identity.unkin.net/application/o/authorize/" token_url: "https://identity.unkin.net/application/o/token/" api_url: "https://identity.unkin.net/application/o/userinfo/" - # Trust the internal unkin.net CA that signs identity.unkin.net's cert - # (mounted from the reflected vault-ca-cert Secret). - tls_client_ca: "/etc/grafana/vault-ca/ca.crt" # Authentik permission groups -> Grafana roles. akP-grafana-admin is granted # to akR-global-admin members (and direct members) via terraform-authentik. role_attribute_path: "contains(ak_groups[*], 'akP-grafana-admin') && 'Admin' || 'Viewer'" From e4d93ef4fec60a58944a7d93dd466b0201e664a8 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 29 Aug 2026 21:52:52 +1000 Subject: [PATCH 13/13] valkey-operator-system: use canonical ghcr.io registry (#437) rke2's `registries.yaml` already rewrites upstream image names to the artifactapi mirror, so manifests must carry canonical upstream names. Only in-house `artifactapi.k8s.syd1.au.unkin.net/docker-internal/...` images stay explicit. Changes: - Set the valkey-operator chart `image.registry` to `ghcr.io`. The `registry`/`repository`/`tag` split is untouched otherwise, so the rendered image is `ghcr.io/valkey-io/valkey-operator:v0.5.0`. `kustomize build --enable-helm apps/overlays/au-syd1/valkey-operator-system` differs from main only in that image string. No other proxied image refs in the file (the `helmCharts[].repo` entry in kustomization.yaml is a Helm chart repo, not a container registry, so it stays on artifactapi). Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/437 Co-authored-by: unkin-agent Co-committed-by: unkin-agent --- apps/overlays/au-syd1/valkey-operator-system/values.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/overlays/au-syd1/valkey-operator-system/values.yaml b/apps/overlays/au-syd1/valkey-operator-system/values.yaml index e1afdf9..cbbd383 100644 --- a/apps/overlays/au-syd1/valkey-operator-system/values.yaml +++ b/apps/overlays/au-syd1/valkey-operator-system/values.yaml @@ -1,5 +1,7 @@ +# rke2's `registries.yaml` rewrites upstream image names to the artifactapi +# mirror, so this manifest carries the canonical upstream registry. image: - registry: artifactapi.k8s.syd1.au.unkin.net/ghcr + registry: ghcr.io repository: valkey-io/valkey-operator tag: v0.5.0