Unload After Sampling
Free the Weights You Already Used, Before the VAE Moves In
- passthrough
- passthrough
The render survives sampling and dies on the decode. Little bit infuriating: the latents are finished, the expensive part is over, and then the VAE tries to load on top of a diffusion model that ComfyUI had no reason to let go of. That's the failure this node is for.
Why the model is still sitting there
ComfyUI's memory manager is genuinely decent at evicting models under VRAM pressure - right up until the last stage of a render. After the sampler finishes, the diffusion model is still staged when the VAE needs its room, ComfyUI logs 0 models unloaded, and the VAE loads on top of it. On a long video clip that's exactly where the OOM lands.
You can't flag your way out of it. --cache-none sounds like the fix and isn't: it clears the node output cache, which is a different structure from ComfyUI's list of currently-loaded models. Two different things with confusingly similar names.
What the node actually does
It's a passthrough with a side effect, and the side effect is the whole product. Reading the shipped source (unload_nodes.py), it walks comfy.model_management.current_loaded_models, takes each entry's model class name, tests it against your keep list, and for anything that isn't kept calls model_unload(None) on the patcher and drops it from ComfyUI's list. Then gc.collect() and soft_empty_cache().
The None argument matters - it skips the partial-unload path so the model gets detached fully rather than half-staged. And a failed unload is caught and logged rather than raised, on the reasoning that a failed unload costs you memory, not your render. You keep the output either way.
That's why this is one of the few memory hacks with no quality dial. It runs after sampling, so the latents are already computed. The weights that produced them are dead memory by the time the node fires. There is no setting here that can make your image worse.
Inputs and outputs that matter
Two of them, and you only really think about the second.
passthrough(*, required) - the thing you drop the node on, normally the LATENT coming out of your sampler.keep(STRING, defaultVAE) - comma-separated substrings of model class names to protect, matched case-insensitively. The tooltip is exact about why the default is what it is: "The VAE runs next, so keep it."
The single output is passthrough (*) - the same object, unchanged. Wire it into VAE Decode. Because both sockets are the wildcard * type, one node does the job in a LATENT link, a CONDITIONING link, or an IMAGE link without you needing a differently-typed version.
Placement, then: on the latent link between sampler and decode. You are inserting a node into an existing wire, not rewiring anything.
Install
Via ComfyUI Manager, search "Unload Models". Manually:
cd ComfyUI/custom_nodes
git clone https://github.com/neezoy/ComfyUI-UnloadModels
Restart ComfyUI. There is nothing to download - pyproject.toml declares an empty dependencies list, there's no requirements.txt, and the only imports are gc, logging, and comfy.model_management. In an ecosystem where "install the node" routinely means "resolve a transformers version conflict," a pack that adds zero packages is a small gift.
Where people get burned
Two-stage workflows. Two-pass, hires-fix and low-res-then-upscale graphs run the sampler twice. Drop this node after the first sampler and you've just freed the model the second one needs - ComfyUI will quietly reload it from disk, and you've paid PCIe twice for nothing. Put it after the last sampling node, before decode.
Expecting a speedup on a card that already fits. The README is honest about this: if your models comfortably fit in VRAM, nothing improves. Freeing and reloading is a net loss. This is an over-budget tool, and the author's own 33.75 → 11.00 s/it number came from a 16GB card running a model stack that needed roughly 36GB. Your mileage is a function of how far over you are.
Trusting other packs' "free VRAM" buttons to do this. Most of them call ComfyUI's own free_memory(). That's not a shortcut around the problem, it is the problem - it's the function that's declining to evict your model.
keep matches class names, not filenames. Not "the thing I loaded," not the node title - the Python class of the loaded model. If something you still need downstream is being yanked, set the log level to DEBUG, which prints each model considered plus the reason it was kept or unloaded, and read what it's actually called. At INFO you get one line per node with the total megabytes freed, which is also the fastest way to confirm the node ran at all and freed something.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| passthrough | * | — | |
| keepopt | STRING | VAE | Comma-separated substrings of class names to keep loaded. Case-insensitive. The VAE runs next, so keep it. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| passthrough | * | — |