Skip to content

Footprint & performance

Every number on this page was measured, not estimated. Anything reasoned rather than measured is labelled as such.

The short answer

Floor64 MB of RAM. Verified: 48 MB is killed before ready, 64 MB reaches it
Comfortable512 MB. 1 GB if the agent does real work through exec
Disk~95 MB installed, plus the install. A fresh data/ is 364 KB
CPUAny. 0.7% of one core at idle — it waits on the network almost all the time
Needs noPostgres, Redis, vector database, GPU, or local model

There is no minimum-viable-model requirement, because Nouride does not host one. Providers are remote endpoints, so RAM is unaffected by which model an agent uses.

Idle

The daemon at rest, gateways connected, no turn in flight.

ShapeRSS idlePeak during bootArtifact
Compiled binary, Linux, 1 agent74.5 MB139.7 MB95 MB installed
Compiled binary, macOS, 0 agents63.7 MB58 MB
Docker image69 MB process / 96 MB container166 MB85 MB image

Sampled every 15 s for a minute: flat, no drift, 12 threads, no child processes.

The boot peak is roughly double the resting figure — module loading and migrations — and it is what a memory limit has to accommodate, not the resting number. A limit below it produces an OOM kill during startup, which reads as a crash loop with no error in the daemon’s own log: the process never gets far enough to write one.

The binary and the image land within ~10 MB of each other, so do not choose between them on memory grounds. Choose on whether the agent should be able to act on the host.

The floor

Run under a hard cgroup limit, checking whether the daemon reaches ready.

LimitResultRSS
256 MBready75.4 MB
128 MBready55.6 MB
96 MBready46.7 MB
64 MBready24.2 MB
48 MBkilled before ready

The runtime adapts to the limit rather than demanding a fixed budget. Given 64 MB it runs the heap in 24 MB; given 256 MB it spreads out to 75 MB. So “it needs 140 MB” is the wrong reading of the boot peak.

Do not deploy at the floor. It is the number that proves the daemon is small, not a recommendation — at 64 MB there is nothing left for a turn.

Agents are almost free

Agent packs loadedRSSBoot peak
174.4 MB140.5 MB
374.7 MB140.4 MB
676.5 MB140.4 MB

Roughly 0.4 MB per idle agent. An agent is a directory of Markdown, not a process: the prompt is assembled per turn and not held resident, and every agent shares one process, one database and one HTTP server.

This is the number to size against when adding agents. It is the wrong number for sizing concurrent work.

A turn under load

Not measured — it needs a live provider key and a real conversation. What bounds it is configuration, so the ceiling is at least knowable:

SettingDefaultWhy it drives memory
tool_max_output_bytes10 MBOne tool call’s output, held to be returned to the model
max_tool_iterations25Tool calls in a single turn
max_conversation_turns200Stored history per conversation
max_context_ratio0.8Share of the model’s context window that gets filled
max_tokens_per_turn16384Response size

Memory scales with concurrent turns, not with agents. The queue is ordered per conversation and parallel across conversations, so ten chats answered at once is ten contexts assembled at once. One agent in ten busy channels costs more than ten agents in one quiet one.

Reasoning, not measurement: a single turn reading a large file or running an exec that prints megabytes is the realistic worst case. That is why the recommendation is 512 MB rather than 128 MB.

--smol

The released binary is compiled with --smol baked into its execArgv, so it runs a smaller heap and collects more often. Measured on two live boxes, before and after the same build otherwise:

RSS beforeRSS afterheap beforeafter
6 agents, 9 gateways76.5 MB65.6 MB48.6 MB41.4 MB
1 agent, 2 gateways95.4 MB79.3 MB46.4 MB33.2 MB

The cost is CPU: an allocation-heavy benchmark shaped like a turn runs 21% slower. That is the right trade here, because a turn’s wall time is dominated by waiting on a model rather than by local allocation — but bun run build:binary --no-smol builds without it for a host with memory to spare.

Build variants

Every combination, measured on one linux-x64 box by the same method.

BuildBinaryTarballRSS idleLicence
(default)80.3 MB74.7 MB51.8 MB
--no-wa-bridge80.3 MB38.4 MB51.8 MB
--bundle-wa-bridge95.0 MB43.5 MB58.0 MBGPL-3.0
--bundle-nougate80.7 MB74.8 MB53.0 MB
both bundles95.3 MB43.6 MB59.7 MBGPL-3.0

RSS idle here is the daemon carrying the code with the feature switched off — what it costs to have the option.

--no-wa-bridge produces a byte-identical binary to the default, same sha256. The 36 MB it saves is the bridge executable dropped from the tarball, not anything in the daemon.

A bundle you do not switch on is not free. The dynamic import means the code is never evaluated, but a bundled module graph is registered either way — which is why the non-bundle builds externalise it rather than relying on the import alone.

Separate process versus bundled

ComponentIts own processBundledSaved
WhatsApp bridge62.2 MB+21.3 MB40.9 MB
LLM gateway (nougate)44.6 MB+6.0 MB38.6 MB
both106.8 MB+27.3 MB79.5 MB

Why the saving is roughly 36 MB either way: a separate process cannot share the Bun runtime. Measured by running one binary twice, ~36 MB of runtime and bundle pages are shared between processes of the same executable, and ~14.5 MB of heap is private to each. So a second process pays the 36 MB again; a second listener or a bundled module pays none of it.

Both separate artefacts are almost entirely Bun: a bare compiled hello-world for linux-x64 is 78.7 MB, so nougate’s own code is 0.3 MB of its 79.0 MB.

Sizing a box

TargetVerdict
Raspberry Pi 4 / 5 (1 GB+)Comfortable. --target linux-arm64. A Pi 5 is more than this needs
Any 512 MB VPSFine, including the dashboard. The cheapest tier at most providers
1 GB VPSThe recommendation. Headroom for exec output and several concurrent chats
Mini PC / NUC / old laptopOverkill, and the right choice anyway if the agent does infra work
NAS with DockerFine. Check uname -m — several are arm64
Alpine / muslNeeds the -musl build and apk add libstdc++

For Kubernetes, a starting point rather than a tuned answer:

resources:
requests:
memory: 128Mi # comfortably above the 64 MB floor
cpu: 50m # 0.7% of a core at idle
limits:
memory: 512Mi # accommodates the boot peak and a turn with large tool output
cpu: "1"

Watch the actual peak before tightening limits.

The daemon is often not the largest process in its own cgroup

Worth knowing when planning a small box. On a live install, wa-bridge measured 67.8 MB and a single Node-based MCP server 91.5 MB, against the daemon’s 73.3 MB.

On a 256 MB device the daemon and the bridge fit. Adding one Node MCP server does not.

Why it is this small

Not tuning. Four architectural choices:

  • Bun/JSC rather than Node/V8. Under memory pressure the measured behaviour is to shrink to fit, rather than to hit a heap ceiling and abort.
  • No external infrastructure. One SQLite file. The daemon links three third-party packages.
  • No browser and no local model. These are what push comparable runtimes into gigabytes.
  • Agents are configuration, not processes. 0.4 MB each, and that is a different category rather than a better implementation of the same one.

Part of the gap is scope. Adding browser automation would erase the measured advantage immediately: a Chromium instance is 300 MB to over 1 GB, four to fifteen times the entire daemon. It is not planned, and the resource profile is only the third reason — the first is that a browser driven by a model taking instructions from a chat message is a general-purpose request engine inside your network boundary, with no equivalent of the exec approval gate. If it is ever wanted, it belongs in an MCP server, out of process and behind a tool boundary that can be refused per agent.