Local Inference v1.0.0 MIT
DS4 Server Health and Operations
Use when operating the DS4 inference server on macOS. It serves DeepSeek models. Covers launchd setup, health checks, safe restarts, model switches, and multi-Mac engine updates.
Features
- Health checks for the process, the listening port, the served model id, and upstream currency.
- A launchd configuration that loads the correct engine flags, the Metal shader environment, and the MTP draft model.
- A restart sequence that disables the supervisor first, clears the lock file, and waits for the model to load.
- A model-switch workflow covering symlink, cache clearing, and disk checks.
- A staged, verified cluster update across build and peer machines.
- A remote diagnosis checklist for hosts you reach over SSH.
Benefits
- Restarts stop producing silent failures, such as a second server losing the port or a stale lock blocking the start.
- Model swaps avoid corrupted answers from a cache built for another variant.
- Cluster updates keep memory from spiking on several machines at once.
- Problems are diagnosed from measured process and port state, not from a remembered plist.
Install
From the tap (or GitHub path)
hermes skills install shagghiesuperstar/hermes-skills/skills/ds4-system-health --category local-inference
Direct URL
hermes skills install https://raw.githubusercontent.com/shagghiesuperstar/hermes-skills/main/skills/ds4-system-health/SKILL.md --category local-inference
Get the skill
Related skills
- Local InferenceDS4 Local Coding WorkbenchUse when two coding agents share a local DeepSeek engine. The engine runs on one Mac. Covers worktree isolation, role split, approval gates, and config for each client.
- Local InferenceLocal Model Deployment on macOSUse when running, debugging, or retiring LLM servers. They run locally on a Mac. Covers port coexistence, single-model residency, auth and bind rules, memory-guard errors, client routing, and safe model decommissioning.
- Local InferenceMLX Inference Servers and Model TuningUse when running or tuning local MLX inference servers. They run on a Mac. Covers safe config edits, restarts, per-model settings, side-by-side variants, and single-GPU sharing.
Related repos
The skill file
DS4 Server Health and Operations
Overview
DS4 is a narrow inference engine for DeepSeek V4 GGUF models on Apple Silicon. It serves an OpenAI-compatible API on one port and keeps its model and Metal shaders resident in unified memory. Under launchd it fails in ways a plain process does not: macOS privacy controls block reads from external volumes, a supervisor respawns the server after a kill, a stale lock file blocks the next start, and a second server competing for the port fails silently. This skill covers the health checks, the restart order that avoids those traps, the model-switch sequence, and an engine update across several Macs.
When to Use
- The DS4 server is down, returns no models, or answers with the wrong model.
- You need to restart the server, switch GGUF variants, or rebuild the engine from source.
- A remote Mac running DS4 is unreachable or its agents report that the primary model failed.
- You are rolling a new engine build out to several machines.
Features
- Health checks for the process, the listening port, the served model id, and upstream currency.
- A launchd configuration that loads the correct engine flags, the Metal shader environment, and the MTP draft model.
- A restart sequence that disables the supervisor first, clears the lock file, and waits for the model to load.
- A model-switch workflow covering symlink, cache clearing, and disk checks.
- A staged, verified cluster update across build and peer machines.
- A remote diagnosis checklist for hosts you reach over SSH.
Benefits
- Restarts stop producing silent failures, such as a second server losing the port or a stale lock blocking the start.
- Model swaps avoid corrupted answers from a cache built for another variant.
- Cluster updates keep memory from spiking on several machines at once.
- Problems are diagnosed from measured process and port state, not from a remembered plist.
Procedure
Health checks
Confirm what is running, what owns the port, and what the server reports:
pgrep -af ds4-server lsof -i :<engine-port> -P -n | grep LISTEN curl -s http://127.0.0.1:<engine-port>/v1/modelsA port being open does not mean the model is loaded. Use the
/v1/modelsresponse as the readiness test.Check upstream currency from the engine source checkout:
cd <engine-src> && git fetch origin echo "behind: $(git rev-list HEAD..origin/main --count)" ls -la <engine-deploy-dir>/ds4-serverA binary older than the latest commits you need means a rebuild.
Audit every launch agent that points at the engine before you load another one. Only one instance can own the port, and the others show a PID that is not serving:
ls ~/Library/LaunchAgents/*.plist | xargs grep -l ds4-server ps -o pid,args -p $(pgrep -f ds4-server | head -1)The arguments of the running process are the truth, not the plist on disk.
Restart safely
For a planned restart, stop the supervisor before killing the server, because a keep-alive agent or a managing parent relaunches the process within about a second:
launchctl bootout gui/$(id -u)/<engine-label> 2>/dev/null || trueIf an agent gateway supervises the engine, switch that gateway to a fallback model first, or stop the gateway.
Kill the process and clear the lock file it leaves behind:
pkill -9 -f ds4-server; sleep 3 rm -f /tmp/ds4.lock pgrep -af "ds4-server|launcher" || echo "clean"Start the server with explicit flags and a working directory where its relative shader paths resolve:
cd <engine-deploy-dir> ./ds4-server -m <model-link>.gguf --ctx 131072 --host 127.0.0.1 --port <engine-port> \ --kv-disk-dir <kv-cache-dir> --kv-disk-space-mb 51200 \ --kv-cache-continued-interval-tokens 2048 --mtp <mtp-draft>.ggufGive the port explicitly. Without it the server can default to the same port as another local server.
Allow about 30 seconds for the model to load, then poll
/v1/modelsuntil it answers:for i in $(seq 1 20); do curl -sf http://127.0.0.1:<engine-port>/v1/models && break; sleep 3; done- Re-enable the supervisor only after the manual start works, or keep it disabled and run the server from a logged-in terminal or a background job.
Launchd configuration
- Keep the binary, the model file, and the Metal shader directory on the internal disk. Launchd-spawned processes cannot read from external or network volumes under macOS privacy rules, and the symptom is a process that hangs inside its first file open with no error output. The KV cache may stay on an external volume because it is memory-mapped.
- Set every
DS4_METAL_*_SOURCEvariable the engine reads in the launch agent. Search the engine source for the patternDS4_METAL_.*_SOURCEto list them. A missing variable fails at startup withMetal source X not found. - Do not put
--imatrixon the command line. Calibration data is baked into the GGUF at conversion time. To use the calibrated variant, point-mat that file.
Switch models
Follow this order. Skipping a step produces corrupted completions or a failed start:
- Stop the server.
- Download the new variant, and confirm the download has finished (no
.incompletefiles). - Point the model symlink at the new file, and confirm with
readlink. - Delete the KV cache files. Cache entries from one calibration are incompatible with another.
- With the operator's approval, delete the old model. Do this only after the symlink is verified and after you check that no other tool still opens the file.
- Check free disk space.
- Start the server and run the readiness test.
Update the engine across several machines
- Run a pre-flight health check on every machine before touching it. Proceed only when free memory is comfortable, swap-out rate is low, and no stuck processes are present. Postpone a machine that is swapping heavily; restarting the engine there can push it into an out-of-memory crash.
- Get explicit go-ahead from the operator, and move every agent that depends on the engine to a fallback model before the rollout.
- Disable the supervisor and kill the server on each machine, then clear the lock file.
Build once on the build host from a reviewed tag or commit, and record the commit SHA. Then distribute the binaries and the Metal shader directory to the peers, and compare each peer's binary hash with the build host's
shasum -a 256output:cd <engine-src> && git fetch --tags && git checkout <reviewed-tag-or-sha> && git rev-parse HEAD && make clean && make -j4 && make ds4-agent scp <engine-src>/ds4-server <engine-src>/ds4-agent <peer>:<engine-deploy-dir>/ rsync -av <engine-src>/metal/ <peer>:<engine-deploy-dir>/metal/Sync the shaders before the binary swap, because the engine reads them at runtime.
- Confirm the model files exist before creating or changing any symlink. A link to a missing file starts a server that crashes at load.
- Restart one machine at a time, about 30 seconds apart, and run the readiness test on each before starting the next.
- Move the agents back to the engine, and confirm each machine answers on
/v1/modelswith the expected id.
Remote diagnosis
- Ping the host first. If ping works but SSH times out, the host is alive and its SSH daemon is starved, so retrying SSH will not help. If ping fails, the host is offline or has panicked. Use an alternate access path only if you have one configured.
- When SSH works, collect the running processes, launch agents, cron entries, and the port owner in one command, then fix the lock file and the agent in that order.
Pitfalls
- Stale lock file blocks every restart. A killed server leaves its lock file behind, and the next start fails with
cannot open modelor exits silently. Remove the lock before each start. - Planned restarts need the supervisor stopped first. A keep-alive agent or a supervising gateway relaunches the engine within a second, so stop it before killing the process.
- Two plists for one binary. Several agent files can load the engine with different flags. Only one wins the port, and the others look healthy in the launch list. Keep the single correct agent and unload or delete the rest.
- Omitting the port flag causes a silent collision. A missing
--portmakes the engine default to the port another local server uses, and one of them silently loses it. - Bind wide only on a private network. Binding to all interfaces makes the engine reachable from the LAN and the private network. Use loopback unless another machine needs it, and never expose the port publicly.
- Port open is not model ready. Model loading takes a short period after start, about 30 seconds for a large model. Poll
/v1/modelsuntil it answers. - Activity Monitor memory is misleading. The launch-agent report can show a tiny resident size while the Metal mapping holds tens of gigabytes. Judge memory with
vm_statandmemory_pressure. - Overcommitted memory kills the machine. Several large model directories loaded at once can exceed physical memory, and the server then fails every request. Keep only the models you use loaded, and keep a healthy margin of free memory for the engine's working set.
- A long-running worker can exhaust swap. A sustained worker process on the same machine can drive swap to its limit and force a reboot. Check the top CPU consumers during health checks.
- Stale binaries and backups are inert. Files named with old suffixes are not running. Confirm that with
pgrepbefore deleting them. - A phantom model returns empty answers. If the configured model id does not match what the server lists in
/v1/models, some servers return empty completions instead of an error. Compare the two before debugging anything else. - Context limits belong at the provider level. If a client rejects a small context window, set the context length in the provider's config. Adding an unrelated minimum key does not help.
- Thinking and token budgets. Long reasoning output consumes the output budget before the answer. Set reasoning output to the minimum your client needs.
- Hugging Face metadata is partial. Per-file timestamps are often empty. Use the file-tree endpoint for sizes, and rely on the repository's card timestamp for recency.
- Multi-token prediction is experimental. The draft model is passed with
--mtp, is not persisted across restarts, and is best suited to greedy decoding. - Strict shell flags hide errors in keep-alive scripts. The script exits on the first failed command, the agent restarts it, and the loop repeats with no useful log. Test the script with
bash -xto find the failing line. - Dashboards show dashes when the health endpoint is missing. A telemetry dashboard that reads a health route shows empty metrics when the engine build does not expose it. Check the route before blaming the dashboard.
Verification
/v1/modelson the engine's port returns the expected model id, and a short chat completion returns text.pgrep -af ds4-servershows exactly one process, and its arguments match the intended flags.lsofshows that one process owns the engine's port.lsshows the binary, the shader directory with 19 shader files, and a model symlink that resolves to a real file.- After a switch or update, the KV cache directory holds no files from the previous variant.
Attribution
- DS4 inference engine by antirez: https://github.com/antirez/ds4