Both agents stopped reading on 2026-08-27 and had read nothing since:
k8s-1 at 12:15:41Z, k8s-2 at 19:14:27Z, each frozen at its line count
(35449 and 29917) while Traefik kept logging normally.
Each had hit its first kubelet log rotation after starting. The paths
under /var/log/containers are symlinks into /var/log/pods, and kubelet
rotates the target at containerLogMaxSize; the inotify tailer reopens
the path, waits for a file that is already present, and never returns.
Nothing about the result looks broken: pods Running and Ready, LAPI
answering, bouncers pulling, cumulative counters still showing large
numbers. The only symptom is that no scenario fires again, which reads
as quiet rather than blind.
So this was not a one-off. Rotation happens roughly every eight hours
here, and the agents die on the first one after every start, which
bounds local detection to a few hours at a time.
CrowdSec diagnoses this itself and names the fix, at agent startup:
File /var/log/containers/traefik-...log is a symlink, but inotify
polling is enabled. Crowdsec will not be able to detect rotation.
Consider setting poll_without_inotify to true in your configuration
Upstream carries the identical case as crowdsec#2449 (k3s + Traefik),
and the chart gained this field in helm-charts#109 to fix it. It does
not conflict with the force_inotify: true the chart hardcodes: that
governs the fsnotify watch on the directory, used to discover the log
of a replacement Traefik pod, while this governs how an already
discovered file is read. Verified by rendering the chart.
The acquisition ConfigMap is checksummed into the agent pod template,
so this rolls the DaemonSet and the stalled agents restart with it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four Paperless requests overlapped and all four died:
decode: failed to find free space in the KV cache
srv decode: Context size has been exceeded.
They wanted 6209, 6472, 4303 and 1271 tokens from a shared pool of
16384. Auto slot count opens four of them and is also what enables
--kv-unified, so they draw on one buffer rather than a quarter each --
generous for a single caller, and impossible for four.
They overlapped because a client timeout does not stop the generation
behind it. The eval abandons a request after 300 s and moves to the next
document; the abandoned one keeps consuming the budget the next one
needs, and one slow document is enough to start the pile-up. With one
slot they queue instead, which is what should happen: nothing calls this
server in parallel on purpose, and Paperless's celery worker runs at
concurrency 1.
Deliberately no liveness probe alongside this. /health answered 200 in
0.4 ms throughout, which looks like the probe failing at its job -- but
the server drained the batch and served again by itself, so a probe that
restarted the pod would have turned a self-healing incident into an
outage. With a single slot, a probe asking for a real completion would
also queue behind live work and kill healthy pods mid-document.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#767 broke the model. Asked to classify a document and answer with JSON
only, the server returned 1025 tokens of "!" against a 650-token prompt.
That is the KV cache emitting numbers rather than language, and it takes
everything downstream with it: Paperless ran past its 300 s timeout on
every document, Home Assistant got an empty completion once and a
hallucinated list of devices that do not exist another time.
None of that looked like a backend fault from the outside. The server
starts, /props reports the requested size, /health passes, and the
OpenAI response is well-formed with finish_reason "stop". Only the
generated text gives it away, which is why three separate symptoms got
blamed on prompt size, on tokenizer behaviour and on the RAG context
before anyone read the output.
The boundary is between the two changes rather than at either: #765 left
16384 per slot and produced coherent answers; #767 raised it to 32768 and
produced this. The same Vulkan/Mesa stack already fails at --ubatch-size
2048 with ErrorDeviceLost, so a ceiling here is not a surprise -- only
that it degrades quietly instead of erroring.
--parallel goes with it. Unset, slots return to auto, which is what keeps
--kv-unified enabled ("if number of slots is auto"), so the auto slots
share one buffer and each may draw the full 16384.
Home Assistant needed 18910 tokens for one Assist request and cannot have
them at this ceiling. That path is closed, and Paperless -- which fits in
16k and never needed more -- is what this restores.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
proxy-2 has been enforcing since 14:21:54 without incident, so the role
default moves to nftables and the host_vars override goes away rather than
being duplicated for proxy-1. proxy-2's rendered config is unchanged by this,
so it will not restart; only proxy-1 does.
The staged rollout did its job and can be read off the deploy log:
proxy-1: deployed in mode "dry-run"
proxy-2: deployed in mode "nftables"
and only proxy-2 restarted, which showed up as a 1,887,882-byte startup pull
from its address alone.
The delivery path was tested in enforcing mode with a decision on 192.0.2.1
(TEST-NET-1): both hosts had the ban 3s later as a 205-byte delta, and the
revocation as a 203-byte delta 1-2s after deletion.
What is still not proven is that a packet is actually dropped, and the
measurement for that argues for finishing the rollout rather than waiting.
Requests from banned addresses reaching Traefik stopped after proxy-2 armed —
1 at 14:02, 3 at 14:07, 22 at 14:08, 1 at 14:18, none after — but 25 of those
27 arrived in a single burst beforehand, so at this volume that is consistent
with enforcement rather than evidence of it. With both hosts armed the claim
becomes falsifiable instead: no request from an address in the decision set
should reach Traefik at all, and a single one would prove otherwise.
dry-run remains available per host through inventory/host_vars, which is what
a newly added proxy should get before it enforces.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`llama-server --help` documents both halves of what #765 got wrong, and
it ships in the image:
-np, --parallel N number of server slots (default: -1, -1 = auto)
-kvu, --kv-unified use single unified KV buffer shared across all
sequences (default: enabled if number of slots
is auto)
So leaving --parallel unset did not merely pick a slot count, it enabled
a shared KV buffer that every slot could draw the full --ctx-size from.
That is why this server reported n_ctx_slot 16384 across four slots, and
the startup log said so outright: kv_unified = 'true'. Naming a slot
count switches that off and splits the context.
The value stays 65536. Only the reasoning in the comment changes, from an
inference drawn off two /props readings to the documented behaviour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#765 claimed --ctx-size was per slot and halved the slot count to pay for
doubling it. Both halves of that were wrong. With --parallel set,
llama.cpp divides --ctx-size across the slots, so 32768 over two slots
gave 16384 each -- exactly what was there before -- and the Assist
request fails with the same message it did then:
request (18910 tokens) exceeds the available context size (16384)
The flag does behave differently without --parallel, which is where the
wrong reading came from: the four-slot server reported n_ctx_slot equal
to --ctx-size rather than a quarter of it. Adding --parallel changed
that, and the memory reading confirms it -- the pod fell from 24979 MiB
to 21601 MiB, so total KV halved rather than holding steady.
65536 across two slots is 32768 each, and puts total KV back where it was
before #765 along with the memory that came with it.
Verify rather than assume this time:
kubectl -n ai exec deploy/llama-server -- curl -sS \
http://127.0.0.1:8080/props | jq .default_generation_settings.n_ctx
must report 32768.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 16:13:35 +02:00
5 changed files with 77 additions and 44 deletions
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.