Why Your VRAM Looks Full Even When ComfyUI Is Idle
Open ComfyUI, load a checkpoint, generate one image, and then just sit there. Open nvidia-smi in a second terminal. The VRAM is still nearly as full as it was during the generation — even though nothing is running.
That residual occupancy is not a leak. It is the ComfyUI browser frontend working exactly as designed. When the web UI starts, it keeps the models you loaded — the diffusion model, the text encoder, and the VAE — resident in GPU memory so that the next “Queue Prompt” click starts instantly. For an interactive workflow where you tweak a prompt and re-render every few seconds, that caching is a feature.
But if you drive ComfyUI as a headless backend — submitting jobs over HTTP and not touching the browser at all — that always-on residency is pure waste. It is memory you are paying for while getting nothing back.
How much exactly? A first-party test by Andrew Zhu on an RTX 5090 measured peak VRAM at 18 GB when running Krea-2-Turbo through the browser, dropping to 13.5 GB for the identical generation driven through a plain Python headless pipeline. That is a ~25% reduction, same model, same image quality (source). On a 16 GB card, 4+ GB is very often the difference between a workflow that fits and one that throws CUDA out of memory.
The Two Modes: Browser UI vs. Headless Server
ComfyUI is, at its core, an HTTP server. The node graph editor you see in a browser tab is just one client that talks to that server. The official documentation is explicit about this: “ComfyUI runs as an HTTP server on your machine by default. You can call it programmatically to submit workflows, upload files, download outputs, and monitor progress” (ComfyUI server documentation).
That means the memory-hungry browser tab is completely optional. You can run the exact same backend in headless mode — no browser, no frontend — and submit the very same workflows as JSON over the HTTP API.
The reason the browser wastes memory specifically comes down to *when* models get freed. There are two distinct behaviors:
1. Browser UI: models stay resident in VRAM after a job completes, because the frontend is waiting for your next interaction.
2. Headless API: you control the lifecycle. Once a workflow finishes and you move on, you can unload models (or let a force-offload node push them back to system RAM / disk) before submitting the next job.
It is the same insight behind every serious local-AI deployment: system memory offloading is a *failsafe*, not an operating mode you want to lean on. The weight held in VRAM between runs is capacity you could be using for higher resolution, larger batches, or a bigger model.
Step-by-Step: Convert a Working Workflow to Headless API Format
Here is the reproducible path. It assumes you already have a working ComfyUI workflow in the browser and want to serve it without the UI.
Step 1 — Export your workflow in API format
The workflow JSON the API accepts is *not* the same file the browser saves. The browser’s “save” format stores UI metadata — node positions, colors, group sizes — that the API has no use for. The API format strips all of that out, leaving a smaller graph that is what /prompt actually consumes. The official docs describe this distinction in the Workflow API Format page.
To convert: open your .json in the frontend via File → Load, then export with File → Export Workflow (API). Do not hand-convert the save format; the node catalog between the two formats diverges in ways that are easy to get wrong. (If your nodes “vanish” after submitting, that is almost always a save-format file being fed to the API — see this workflow.json vs workflow_api.json walkthrough.)
Step 2 — Start the server in headless mode
Launch ComfyUI with the frontend disabled:
python main.py --listen 127.0.0.1 --port 8188
You do not need a special --headless flag for memory purposes — headless here means “don’t open a browser tab,” not a separate binary. What matters is that *you* never keep the frontend open against this instance. Do not navigate to http://127.0.0.1:8188 in a browser. Submit everything over HTTP.
For extra insurance, add your normal VRAM hygiene flags. If you are on a memory-constrained card, --disable-smart-memory prevents ComfyUI from preemptively loading more than it needs, and --reserve-vram carves out headroom for the OS. These flags are covered in detail in this VRAM launch-flag explainer.
Step 3 — Submit the workflow over HTTP
Post the API-format workflow to the /prompt endpoint as JSON with a "prompt" key wrapping your graph, plus a unique client_id:
curl -X POST http://127.0.0.1:8188/prompt \
-H "Content-Type: application/json" \
-d '{"prompt": <your-api-format-graph>, "client_id": "headless-runner"}'
A successful call returns a prompt_id. You then poll /history/{prompt_id} until the status flips to completed, then download the output images/videos from the returned filename fields. The full run-job-then-poll loop — which is exactly what you need for a reliable headless harness — is broken down step by step in this ComfyUI API automation harness guide.
Step 4 — Unload between jobs
This is the part that actually gives you the VRAM back. In an interactive browser session, the models never leave. In a headless loop, *you* decide when they do. Options:
- Use a
force_offload=Trueoption on model-loading nodes (many custom-node video/image suites expose this) so weights return to system RAM immediately after the node runs. - Explicitly free VRAM between batches by letting the Python process that owns the next request manage the lifecycle, rather than keeping a single long-lived session.
Either way, the point is the same: the browser’s “keep it warm” behavior is the thing you are opting out of.
Common Pitfalls When You Go Headless
Pitfall 1 — Submitting save-format JSON. The single most common “why did my nodes disappear” error. Always export via *Export Workflow (API)*, never the plain save file.
Pitfall 2 — Expecting /prompt to be synchronous. It is not. /prompt enqueues the job and returns immediately with a prompt_id. If your script assumes the response body contains finished images, it will read garbage. You must poll /history or listen on the WebSocket (/ws?clientId=...).
Pitfall 3 — Keeping a browser tab open “just to check progress.” The moment you load the frontend against your headless instance, you re-introduce the resident-model behavior you just removed. Monitor via nvidia-smi or the WebSocket events, not the web UI.
Pitfall 4 — Forgetting the VRAM flags on a 16 GB card. Going headless helps, but it does not make a 24 GB checkpoint fit in 16 GB by itself. Headless reclaims the *idle* residency; it does not shrink the *peak* footprint of a single large forward pass. Combine headless mode with your model’s own offload/quantization path — for Wan 2.2 specifically, BlockSwap and offload_device are the levers, covered in this Wan 2.2 Animate 14B on 16GB VRAM guide.
How to Verify the Reclaim Worked
Do not trust the memory back until you measure it. Run nvidia-smi (or gpustat) at three checkpoints under your *headless* setup:
1. Before submitting — right after the previous job completed and your script moved on. VRAM should be visibly lower than what it was mid-generation.
2. Mid-generation — the peak. This tells you the true working-set size of one forward pass.
3. After the next job completes — confirms the residency did not creep back up.
The number you care about is the *idle* delta: browser-mode idle minus headless idle. That is the memory you reclaimed — on the RTX 5090 test it was roughly 4.5 GB, and on a 16 GB consumer card the same effect can turn a borderline OOM workflow into a stable one.
Conclusion
ComfyUI is an HTTP server first and a visual editor second. The browser tab is genuinely useful for prototyping — but once a workflow is stable, keeping the frontend open means handing a quarter of your VRAM to a UI you are not using. Export the API-format JSON, launch the server without a browser, submit over /prompt, and take back the idle residency by unloading between jobs. It is the single lowest-effort VRAM win available on memory-constrained hardware, and it costs nothing but a few lines of Python.
Leave a Reply