Field note · DGX Spark
Detecting which inference engine owns a port: vLLM vs. llama.cpp
Detection that infers the engine from the port number breaks the moment either engine moves. Moving llama.cpp onto the port normally used by vLLM mislabeled it, and downstream code silently wrote null. /props is a clean discriminator: llama-server serves it, and the vLLM OpenAI-compatible server has no such route and returns 404. A second gotcha in the same detour: docker ps --filter publish= does not match a container using host networking.
- Hardware
- NVIDIA DGX Spark · GB10 (SM121) · aarch64 · 128 GB unified
- Software
- vLLM OpenAI-compatible server (port 8000) and llama.cpp llama-server (port 8001) on the same host
Symptom
This host's convention is vLLM on port 8000 and llama.cpp on port 8001. This isn't a bug specific to that convention or to either engine — it will bite anyone who runs two inference engines and identifies them by which port they happen to be on. It bit here when llama.cpp was briefly moved onto port 8000, and the model-registration script mislabeled it immediately, because detection inferred the engine from the port, not from anything the server itself said:
# Check vLLM (port 8000) <-- labels ANYTHING on 8000 as vllm
That's not cosmetic. Downstream code branched on the resulting provider value to scrape --max-num-seqs from a vLLM process that no longer existed on that port, and wrote null.
Fix: ask the server, do not guess from the port
/props is a clean discriminator. llama-server serves it — build_info, chat_template, slots — and the vLLM OpenAI-compatible server has no such route at all:
if curl -s "http://127.0.0.1:${port}/props" | jq -e '.build_info' >/dev/null 2>&1; then
provider="llamacpp"
else
provider="vllm"
fi
A second gotcha in the same detour
docker ps --filter publish=8000 doesn't match a container running with host networking — the vLLM container here does, so its Ports column is empty and the filter finds nothing, even while vLLM is plainly listening on that port. Match on the container name instead.
The port move itself was reverted; nothing in the local tooling actually hardcoded 8000. The one field consumers read is rewritten on every model switch, so it follows whichever server is actually running rather than pinning a port — which is exactly why detecting the engine from the port number was never a safe shortcut to begin with.