Der consume-CronJob hängt das consume-Volume ein, hat aber keine Platzierungsregel. Er landet damit auf einem anderen Knoten als der StatefulSet-Pod, und das erzeugt in cluster-config einen Dauerfehler am Mount.
Symptom, im Cluster gemessen (08.10.2026)
Beide Instanzen mit eingeschalteter SMB-Verbindung melden alle rund zwei Minuten:
MountVolume.SetUp failed for volume "pvc-8b7b112e-…" : rpc error: code = Aborted
desc = no Pending workload pods for volume pvc-8b7b112e-… to be mounted:
map[Running:[paperless-jk-paperless-ngx-sfs-0]
Succeeded:[paperless-jk-paperless-ngx-consume-cronjob-29855520-4bthr
paperless-jk-paperless-ngx-consume-cronjob-29856240-cbqgr
paperless-jk-paperless-ngx-consume-cronjob-29856960-v4fkp]]
betroffen
paperless-jk und paperless-ngx (tb) — je 291 Meldungen in 10 Stunden
gemeldet von
dem kubelet des Knotens, auf dem der StatefulSet-Pod läuft (jk-worker-1)
begonnen
08.10.2026 00:00:33 — mit dem Mitternachtslauf des CronJobs
Die Kette
smbConnection.enabled: true
-> templates/paperless-consume.cronjob.yaml wird gerendert, schedule "0 0,12 * * *"
-> der Pod fuehrt aus: mv /mnt/smb/*.pdf /mnt/consume
und haengt dafuer BEIDE Volumes ein, darunter das consume-Volume (RWX, Longhorn)
-> keine affinity, kein nodeSelector -> Scheduler setzt ihn irgendwohin
(gemessen: jk-worker-2 und jk-worker-3, waehrend der StatefulSet-Pod auf
jk-worker-1 liegt)
-> successfulJobsHistoryLimit (Kubernetes-Vorgabe 3) haelt drei erledigte Pods
-> Longhorns CSI-Treiber verlangt vor dem Einhaengen eines RWX-Volumes einen
Workload-Pod in PENDING. Vorhanden sind einer in Running und drei in
Succeeded -> Aborted, und das bei jedem Versuch des kubelets.
Die Gegenprobe steht im Cluster:paperless-cmk hat smbConnection.enabled: false, also keinen CronJob, also nur den StatefulSet-Pod in Longhorns Workload-Liste — und null solche Ereignisse. Gleiche Chart-Fassung, gleiche Volume-Bauart.
Warum das nicht in cluster-config zu lösen ist
templates/paperless-consume.cronjob.yaml nimmt aus den Werten genau einen Schlüssel: smbConnection.cronJob (den Zeitplan). Keine affinity, kein nodeSelector, keine tolerations. Betroffene Fassung: 12.2.0+up3.2.1.
Vorschlag — und ein statischer nodeSelector wäre der falsche Griff
Der StatefulSet-Pod ist selbst nicht festgenagelt: er liegt da, wo der Scheduler ihn hingelegt hat, und wandert bei jedem Neustart. Ein fester nodeSelector am CronJob würde also genau so lange stimmen, bis der StatefulSet-Pod umzieht — und dann still wieder falsch sein.
Das passende Mittel ist eine podAffinity auf den StatefulSet-Pod mit topologyKey: kubernetes.io/hostname. Sie folgt dem Pod, wohin er auch geht. Das Label existiert bereits und ist am laufenden Pod abgelesen: app: <release>-<chart>-sfs.
Die Abwägung, die dazugehört, und sie ist nicht klein:
Mit required wird der CronJob-Pod unschedulebar, wenn der StatefulSet-Pod nicht läuft — etwa bei replicas: 0 oder während eines Neustarts. Er bleibt dann Pending stehen, und alle zwölf Stunden kommt einer dazu. Dagegen helfen startingDeadlineSeconds und concurrencyPolicy: Forbid; ob das in den Chart gehört oder in die Werte, ist Teil der Entscheidung.
Mit preferred fällt er im Zweifel auf einen anderen Knoten zurück — und ist damit genau der Fehler, den dieses Issue beschreibt, nur seltener und schwerer zu finden. Ich halte preferred hier für die schlechtere Wahl, weil der stille Rückfall das Fehlerbild erzeugt, das zehn Stunden lang niemandem auffiel.
Falls die Platzierung konfigurierbar werden soll statt fest verdrahtet, wäre die Form smbConnection.cron.affinity (oder ein null-sicherer Helfer wie bei den übrigen Charts) der Ort — mit der podAffinity als Vorgabe, nicht als leeres Feld.
Die Alternative, die das Problem an der Wurzel nimmt
Den mv nicht in einem eigenen Pod laufen lassen, sondern im StatefulSet-Pod — als Sidecar mit Schlafschleife oder über ein exec. Dann wird das consume-Volume nur von einem Knoten eingehängt und bräuchte nicht einmal RWX.
Das ist der sauberere Entwurf und der größere Eingriff: Er ändert, wofür das consume-Volume RWX ist, und RWX lässt sich an einer bestehenden PVC nicht zurücknehmen. Für die drei laufenden Installationen bliebe es also bei RWX, und der Gewinn wäre nur der entfallene Fernzugriff. Deshalb nenne ich es als Alternative und nicht als Vorschlag.
Was nicht hilft
successfulJobsHistoryLimit: 0. Longhorns Prüfung will einen Pod in Pending, nicht die Abwesenheit erledigter — mit nur einem Running-Pod bricht sie genauso ab.
Was der Betreiber heute tun muss
Den StatefulSet-Pod löschen. Der neue ist Pending, genau das, was die Prüfung sehen will, und die Schleife endet:
kubectl -n paperless-jk delete pod paperless-jk-paperless-ngx-sfs-0
Beim nächsten Lauf um 12:00 kommt es wieder. Das ist der Grund, warum der Fix in den Chart gehört und nicht in ein Runbook.
Der consume-CronJob hängt das consume-Volume ein, hat aber keine Platzierungsregel. Er landet damit auf einem anderen Knoten als der StatefulSet-Pod, und das erzeugt in `cluster-config` einen Dauerfehler am Mount.
## Symptom, im Cluster gemessen (08.10.2026)
Beide Instanzen mit eingeschalteter SMB-Verbindung melden alle rund zwei Minuten:
```
MountVolume.SetUp failed for volume "pvc-8b7b112e-…" : rpc error: code = Aborted
desc = no Pending workload pods for volume pvc-8b7b112e-… to be mounted:
map[Running:[paperless-jk-paperless-ngx-sfs-0]
Succeeded:[paperless-jk-paperless-ngx-consume-cronjob-29855520-4bthr
paperless-jk-paperless-ngx-consume-cronjob-29856240-cbqgr
paperless-jk-paperless-ngx-consume-cronjob-29856960-v4fkp]]
```
| | |
|---|---|
| betroffen | `paperless-jk` und `paperless-ngx` (tb) — **je 291 Meldungen in 10 Stunden** |
| gemeldet von | dem kubelet des Knotens, auf dem der **StatefulSet**-Pod läuft (`jk-worker-1`) |
| begonnen | 08.10.2026 **00:00:33** — mit dem Mitternachtslauf des CronJobs |
## Die Kette
```
smbConnection.enabled: true
-> templates/paperless-consume.cronjob.yaml wird gerendert, schedule "0 0,12 * * *"
-> der Pod fuehrt aus: mv /mnt/smb/*.pdf /mnt/consume
und haengt dafuer BEIDE Volumes ein, darunter das consume-Volume (RWX, Longhorn)
-> keine affinity, kein nodeSelector -> Scheduler setzt ihn irgendwohin
(gemessen: jk-worker-2 und jk-worker-3, waehrend der StatefulSet-Pod auf
jk-worker-1 liegt)
-> successfulJobsHistoryLimit (Kubernetes-Vorgabe 3) haelt drei erledigte Pods
-> Longhorns CSI-Treiber verlangt vor dem Einhaengen eines RWX-Volumes einen
Workload-Pod in PENDING. Vorhanden sind einer in Running und drei in
Succeeded -> Aborted, und das bei jedem Versuch des kubelets.
```
**Die Gegenprobe steht im Cluster:** `paperless-cmk` hat `smbConnection.enabled: false`, also keinen CronJob, also nur den StatefulSet-Pod in Longhorns Workload-Liste — und **null** solche Ereignisse. Gleiche Chart-Fassung, gleiche Volume-Bauart.
## Warum das nicht in `cluster-config` zu lösen ist
`templates/paperless-consume.cronjob.yaml` nimmt aus den Werten genau einen Schlüssel: `smbConnection.cronJob` (den Zeitplan). Keine `affinity`, kein `nodeSelector`, keine `tolerations`. Betroffene Fassung: **12.2.0+up3.2.1**.
## Vorschlag — und ein statischer `nodeSelector` wäre der falsche Griff
Der StatefulSet-Pod ist selbst **nicht** festgenagelt: er liegt da, wo der Scheduler ihn hingelegt hat, und wandert bei jedem Neustart. Ein fester `nodeSelector` am CronJob würde also genau so lange stimmen, bis der StatefulSet-Pod umzieht — und dann still wieder falsch sein.
Das passende Mittel ist eine **podAffinity auf den StatefulSet-Pod** mit `topologyKey: kubernetes.io/hostname`. Sie folgt dem Pod, wohin er auch geht. Das Label existiert bereits und ist am laufenden Pod abgelesen: `app: <release>-<chart>-sfs`.
```yaml
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
topologyKey: kubernetes.io/hostname
```
**Die Abwägung, die dazugehört, und sie ist nicht klein:**
- Mit `required` wird der CronJob-Pod **unschedulebar**, wenn der StatefulSet-Pod nicht läuft — etwa bei `replicas: 0` oder während eines Neustarts. Er bleibt dann `Pending` stehen, und alle zwölf Stunden kommt einer dazu. Dagegen helfen `startingDeadlineSeconds` und `concurrencyPolicy: Forbid`; ob das in den Chart gehört oder in die Werte, ist Teil der Entscheidung.
- Mit `preferred` fällt er im Zweifel auf einen anderen Knoten zurück — und ist damit genau der Fehler, den dieses Issue beschreibt, nur seltener und schwerer zu finden. **Ich halte `preferred` hier für die schlechtere Wahl**, weil der stille Rückfall das Fehlerbild erzeugt, das zehn Stunden lang niemandem auffiel.
Falls die Platzierung konfigurierbar werden soll statt fest verdrahtet, wäre die Form `smbConnection.cron.affinity` (oder ein null-sicherer Helfer wie bei den übrigen Charts) der Ort — mit der podAffinity als Vorgabe, nicht als leeres Feld.
## Die Alternative, die das Problem an der Wurzel nimmt
Den `mv` nicht in einem eigenen Pod laufen lassen, sondern **im StatefulSet-Pod** — als Sidecar mit Schlafschleife oder über ein `exec`. Dann wird das consume-Volume nur von einem Knoten eingehängt und bräuchte nicht einmal RWX.
Das ist der sauberere Entwurf und der größere Eingriff: Er ändert, wofür das consume-Volume RWX ist, und RWX lässt sich an einer bestehenden PVC nicht zurücknehmen. Für die drei laufenden Installationen bliebe es also bei RWX, und der Gewinn wäre nur der entfallene Fernzugriff. Deshalb nenne ich es als Alternative und nicht als Vorschlag.
## Was nicht hilft
`successfulJobsHistoryLimit: 0`. Longhorns Prüfung will einen Pod in **Pending**, nicht die Abwesenheit erledigter — mit nur einem Running-Pod bricht sie genauso ab.
## Was der Betreiber heute tun muss
Den StatefulSet-Pod löschen. Der neue ist `Pending`, genau das, was die Prüfung sehen will, und die Schleife endet:
```bash
kubectl -n paperless-jk delete pod paperless-jk-paperless-ngx-sfs-0
```
Beim nächsten Lauf um 12:00 kommt es wieder. Das ist der Grund, warum der Fix in den Chart gehört und nicht in ein Runbook.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Der consume-CronJob hängt das consume-Volume ein, hat aber keine Platzierungsregel. Er landet damit auf einem anderen Knoten als der StatefulSet-Pod, und das erzeugt in
cluster-configeinen Dauerfehler am Mount.Symptom, im Cluster gemessen (08.10.2026)
Beide Instanzen mit eingeschalteter SMB-Verbindung melden alle rund zwei Minuten:
paperless-jkundpaperless-ngx(tb) — je 291 Meldungen in 10 Stundenjk-worker-1)Die Kette
Die Gegenprobe steht im Cluster:
paperless-cmkhatsmbConnection.enabled: false, also keinen CronJob, also nur den StatefulSet-Pod in Longhorns Workload-Liste — und null solche Ereignisse. Gleiche Chart-Fassung, gleiche Volume-Bauart.Warum das nicht in
cluster-configzu lösen isttemplates/paperless-consume.cronjob.yamlnimmt aus den Werten genau einen Schlüssel:smbConnection.cronJob(den Zeitplan). Keineaffinity, keinnodeSelector, keinetolerations. Betroffene Fassung: 12.2.0+up3.2.1.Vorschlag — und ein statischer
nodeSelectorwäre der falsche GriffDer StatefulSet-Pod ist selbst nicht festgenagelt: er liegt da, wo der Scheduler ihn hingelegt hat, und wandert bei jedem Neustart. Ein fester
nodeSelectoram CronJob würde also genau so lange stimmen, bis der StatefulSet-Pod umzieht — und dann still wieder falsch sein.Das passende Mittel ist eine podAffinity auf den StatefulSet-Pod mit
topologyKey: kubernetes.io/hostname. Sie folgt dem Pod, wohin er auch geht. Das Label existiert bereits und ist am laufenden Pod abgelesen:app: <release>-<chart>-sfs.Die Abwägung, die dazugehört, und sie ist nicht klein:
requiredwird der CronJob-Pod unschedulebar, wenn der StatefulSet-Pod nicht läuft — etwa beireplicas: 0oder während eines Neustarts. Er bleibt dannPendingstehen, und alle zwölf Stunden kommt einer dazu. Dagegen helfenstartingDeadlineSecondsundconcurrencyPolicy: Forbid; ob das in den Chart gehört oder in die Werte, ist Teil der Entscheidung.preferredfällt er im Zweifel auf einen anderen Knoten zurück — und ist damit genau der Fehler, den dieses Issue beschreibt, nur seltener und schwerer zu finden. Ich haltepreferredhier für die schlechtere Wahl, weil der stille Rückfall das Fehlerbild erzeugt, das zehn Stunden lang niemandem auffiel.Falls die Platzierung konfigurierbar werden soll statt fest verdrahtet, wäre die Form
smbConnection.cron.affinity(oder ein null-sicherer Helfer wie bei den übrigen Charts) der Ort — mit der podAffinity als Vorgabe, nicht als leeres Feld.Die Alternative, die das Problem an der Wurzel nimmt
Den
mvnicht in einem eigenen Pod laufen lassen, sondern im StatefulSet-Pod — als Sidecar mit Schlafschleife oder über einexec. Dann wird das consume-Volume nur von einem Knoten eingehängt und bräuchte nicht einmal RWX.Das ist der sauberere Entwurf und der größere Eingriff: Er ändert, wofür das consume-Volume RWX ist, und RWX lässt sich an einer bestehenden PVC nicht zurücknehmen. Für die drei laufenden Installationen bliebe es also bei RWX, und der Gewinn wäre nur der entfallene Fernzugriff. Deshalb nenne ich es als Alternative und nicht als Vorschlag.
Was nicht hilft
successfulJobsHistoryLimit: 0. Longhorns Prüfung will einen Pod in Pending, nicht die Abwesenheit erledigter — mit nur einem Running-Pod bricht sie genauso ab.Was der Betreiber heute tun muss
Den StatefulSet-Pod löschen. Der neue ist
Pending, genau das, was die Prüfung sehen will, und die Schleife endet:Beim nächsten Lauf um 12:00 kommt es wieder. Das ist der Grund, warum der Fix in den Chart gehört und nicht in ein Runbook.