Nodes/Unload Models/Unload CPU Models
ComfyUI Node

Unload CPU Models

The Text Encoder Hogging Your RAM for the Whole Render

By neezoy·Created 2 months ago·Updated 2 months ago· 3
Unload CPU Models
  • passthrough
  • passthrough
◄keep►

Here's the thing that makes "my card is out of VRAM" wrong about half the time in 2026: the biggest thing in your machine isn't on the card. It's the text encoder, parked in system RAM, finished with its one job, and ComfyUI will not evict it no matter how tight things get.

The author's own case: a 15.7GB Qwen3-VL encoder that runs once to produce the conditioning for MiniMax H3, then sits in RAM for the entire render while a 21GB diffusion model hunts for room. Peak demand about 36GB against 26GB available. That's not a VRAM problem, that's a budget problem, and the fix isn't a smaller quant.

Why nothing else frees it

There's a device filter in comfy/model_management.py:

if device is None or shift_model.device == device:

free_memory() only considers models sitting on the device it's currently freeing. A text encoder loaded to the CPU is never on that device, so it's never a candidate for eviction. It doesn't matter that nothing will ask it for anything again - the manager isn't making a judgement call, it's not even looking.

This is a known-good pattern and it's already in the KB: LTX-2's launch was defined by the Gemma 3 12B encoder failing to offload properly and OOMing 16GB cards "almost instantly," and the whole node category that sprang up around it was about encoding once and reusing the result rather than re-loading a 22GB encoder. Frameworks that do this well - the LLM-in-ComfyUI nodes - unload and reload deliberately between models. ComfyUI core just doesn't, for off-device models.

What the node does

It's a passthrough with a side effect. The shipped code grabs the render device with mm.get_torch_device(), walks comfy.model_management.current_loaded_models, and calls model_unload(None) on anything whose loaded.device isn't the render device - then removes it from the list, gc.collect(), soft_empty_cache().

In practice: the text encoder. Anything already on the render device is deliberately left alone, on the reasoning that it's either in use right now or it's what ComfyUI's own eviction is already handling. The None argument to model_unload matters - it skips the partial-unload path and detaches the model fully. Failures are caught and logged rather than raised, so a bad unload costs you memory, not the render.

Inputs and outputs

  • passthrough (*, required) - normally the CONDITIONING coming out of your text encoder.
  • keep (STRING, default empty) - comma-separated substrings of model class names to protect, case-insensitive.

Output is a single passthrough (*): the same object back, untouched. Wire it straight into your sampler's positive/negative conditioning.

That passthrough is what makes it a five-second install instead of a rewire. You're dropping a node into a wire that already exists, and the data on that wire is unchanged - the conditioning was computed before the node ran, which is precisely why freeing the encoder afterwards is safe.

Install

Manager → search "Unload Models". Or:

cd ComfyUI/custom_nodes
git clone https://github.com/neezoy/ComfyUI-UnloadModels

Restart ComfyUI. No dependencies, no model downloads - pyproject.toml declares an empty dependencies list, imports are gc, logging, and comfy.model_management. It's a 6KB file.

Where people get burned

Nothing happens, and the log says so. If your encoder was already loaded to the card rather than the CPU, there is nothing off-device to free and you get UnloadCPUModels: nothing to free at INFO. Set the log level to DEBUG to see each model considered and the reason it was kept or unloaded. The INFO line per node with the total freed is the quickest sanity check that the node fired at all.

It frees everything off-device, not just the encoder. If you deliberately parked something else on the CPU, this takes it. That's what keep is for - but note it matches substrings of the model's class name, not a filename or a node title, so check the DEBUG log for the real name before you type it in.

Assuming other packs' "free VRAM" buttons cover this. They mostly call ComfyUI's free_memory(), which is the exact function skipping your encoder. Freeing the node cache and clearing the loaded-model list are different operations.

Expecting a win on a card that fits. The gain comes from avoiding PCIe transfers, so it scales with how badly you're over budget and vanishes when you aren't. The 33.75 → 11.00 s/it figure in the README is one machine - a Radeon RX 9070 XT on ROCm 7.1, ComfyUI 0.33.0, res_multistep, same seed and resolution both runs. Real, and narrow. Nothing in the pack is AMD-specific, video-specific, or MiniMax-specific; the pattern just shows up hardest there because video encoders are enormous and used once.

Categoryadvanced/memory

Inputs (2)

NameTypeDefaultDescription
passthrough*—
keepoptSTRINGComma-separated substrings of class names to keep loaded. Case-insensitive.

Outputs (1)

NameTypeDescription
passthrough*—