Problem
When memory.backend = "qmd" is configured, OpenClaw spawns a new qmd process for every operation (qmd query, qmd update, qmd embed) via child_process.spawn() in QmdMemoryManager.runQmd().
QMD runs on Bun + node-llama-cpp and loads multiple GGUF models (embedding ~300M, query-expansion ~1.7B, reranker ~600M). Each spawn triggers a full model reload cycle:
- Memory spike: peak ~6.6 GB per process
- Bun v1.3.9 + node-llama-cpp native modules = segfault under concurrent spawns (multi-threaded model loading crashes)
- Latency: cold-start model loading adds seconds to every search
This makes memory.backend = "qmd" essentially unusable on Linux with the current spawn-per-query architecture.
Solution: QMD Daemon Mode
QMD already supports a daemon/server mode (MCP over HTTP on a configurable port). When running as a daemon:
- Models are loaded once and stay resident in memory
- Queries complete in ~1.8s (vs timeout/crash with spawn)
- Hybrid search (BM25 + semantic + reranker) works perfectly
- No repeated Bun startup overhead
Proposed config surface
memory: {
backend: "qmd",
qmd: {
// New: connect to a running QMD daemon instead of spawning
daemon: {
enabled: true,
url: "http://localhost:8181", // or unix socket
// Optional: auto-start daemon if not running
autoStart: true
},
// Existing spawn config still works as fallback
command: "qmd",
}
}
Implementation options
- HTTP client in
runQmd(): When daemon.enabled, replace spawn() with HTTP requests to the daemon endpoint
- Auto-lifecycle: Optionally start/stop the daemon with the gateway (like how
qmd update/qmd embed already run on boot)
- Fallback: If daemon is unreachable, fall back to spawn mode (or builtin SQLite)
Environment
- OpenClaw: 2026.2.9
- OS: Ubuntu Linux 6.17.0-14-generic (x64)
- QMD: installed via
bun install -g
- Bun: v1.3.9
- No GPU (CPU-only inference)
Workaround
Currently using a wrapper script that proxies qmd query calls to the daemon via HTTP, configured as memory.qmd.command.
Problem
When
memory.backend = "qmd"is configured, OpenClaw spawns a newqmdprocess for every operation (qmd query,qmd update,qmd embed) viachild_process.spawn()inQmdMemoryManager.runQmd().QMD runs on Bun +
node-llama-cppand loads multiple GGUF models (embedding ~300M, query-expansion ~1.7B, reranker ~600M). Each spawn triggers a full model reload cycle:This makes
memory.backend = "qmd"essentially unusable on Linux with the current spawn-per-query architecture.Solution: QMD Daemon Mode
QMD already supports a daemon/server mode (MCP over HTTP on a configurable port). When running as a daemon:
Proposed config surface
Implementation options
runQmd(): Whendaemon.enabled, replacespawn()with HTTP requests to the daemon endpointqmd update/qmd embedalready run on boot)Environment
bun install -gWorkaround
Currently using a wrapper script that proxies
qmd querycalls to the daemon via HTTP, configured asmemory.qmd.command.