ComfyUI Extension: comfyui-gpu-resident-loader
Run ComfyUI workflows without the setup
No installs, no CUDA version roulette, no GPU sitting idle on your bill. Bring a workflow and run it in the browser.
ComfyUI custom nodes and startup patches for GPU-resident model loading and sticky VRAM residency
Looking for a different extension?
Custom Nodes (20)
- Checkpoint Clip Loader Resident
- Checkpoint Loader Resident
- Checkpoint Model Loader Resident
- Checkpoint VAE Loader Resident
- Diffusion Model Loader Resident
- Diffusion Model Selector Resident
- Evict CLIP From GPU
- Evict Model From GPU
- Evict VAE From GPU
- Pin CLIP Residency
- Pin Model Residency
- Pin VAE Residency
- Preload CLIP To GPU
- Preload Model To GPU
- Preload VAE To GPU
- Registry Snapshot
- Report CLIP Residency
- Report Model Residency
- Report VAE Residency
- Set Global Residency Policy
README
ComfyUI GPU Resident Loader
A ComfyUI custom-node pack for faster time-to-VRAM, selective safetensors loading, sticky GPU residency control, and visibility into compatible external GPU model caches.
This repo does three related jobs:
- Installs startup-time monkey patches before any workflow nodes run.
- Ships KJ-style resident loader nodes for diffusion models and checkpoints.
- Maintains a live residency system for native ComfyUI objects and compatible external caches, with preload / pin / evict / report controls for native tracked objects and automatic snapshot support plus provider-specific eviction for compatible external entries.
It is not just a “clean RAM” addon. The main target is the path from model file -> tensors -> live ComfyUI object -> VRAM retention, including GPU-resident caches that live outside ComfyUI’s normal loaded-model list.
Why this exists
ComfyUI’s default behavior mixes together two separate concerns:
- ingest path — where tensors are first materialized while a model is being read, and
- residency policy — where the finished model tends to live afterwards.
Those are not the same problem.
This repo focuses on both:
- For
.safetensors, it tries to keep eligible loads on the narrowest, most GPU-friendly path it can. - For resident diffusion and checkpoint-model loads, it avoids broad checkpoint materialization by selecting only the detected UNet keys where possible.
- For runtime VRAM pressure, it adds a sticky-priority registry and teaches ComfyUI’s unload path to protect higher-value resident entries until enough VRAM must be reclaimed.
- For compatible external GPU caches that bypass
comfy.model_management.current_loaded_models, it can discover supported providers at runtime and include their entries in snapshot output and provider-specific eviction decisions, with automatic trim kept opt-in. - For manual control, it exposes nodes that let you preload, pin, evict, and inspect tracked native models, CLIPs, and VAEs.
What changes at startup
__init__.py calls startup.install_patches(), which applies the core monkey patches exactly once when the custom node is imported.
Those startup patches cover the built-in ComfyUI load and residency paths below. Compatible external-cache integrations are installed lazily later, on demand, when a supported module is actually present in the running process.
Patched functions / methods
Current patch surface:
comfy.utils.load_torch_filecomfy.clip_vision.load_torch_file(redirected to the patchedcomfy.utils.load_torch_filewhen present)comfy.model_management.free_memorycomfy.model_management.load_models_gpucomfy.model_management.unet_offload_devicecomfy.model_management.text_encoder_offload_devicecomfy.model_management.vae_offload_devicecomfy.model_management.text_encoder_devicecomfy.model_management.vae_devicecomfy.model_management.unet_inital_load_devicecomfy.model_management.LoadedModel.model_unloadcomfy.model_patcher.ModelPatcher.detachcomfy.sd.load_checkpoint_guess_configcomfy.sd.load_diffusion_modelcomfy.sd.load_clipcomfy.sd.VAE.encodecomfy.sd.VAE.decodecomfy.clip_vision.loadcomfy.controlnet.load_controlnetcomfy.diffusers_load.load_diffusers
Lazily installed external integrations
Current external integration surface:
- compatible SeedVR2
src/core/model_cache.pymodules discovered at runtime
When a compatible SeedVR2 cache module is present, the repo wraps:
GlobalModelCache.set_ditGlobalModelCache.set_vaeGlobalModelCache.replace_dit(when that method exists in the installed SeedVR2 build)GlobalModelCache.replace_vae(when that method exists in the installed SeedVR2 build)GlobalModelCache.remove_ditGlobalModelCache.remove_vae
That lazy integration lets the loader:
- mirror SeedVR2-owned cached DiT and VAE objects into a separate external residency registry
- refresh byte / device / claimed-state metadata from the live cached object
- evict those entries through SeedVR2’s own removal path instead of assuming they live in
comfy.model_management.current_loaded_models
What those patches do
1) load_torch_file becomes residency-aware
The patched loader:
- detects the active load context (
model,clip,vae,checkpoint, etc.) - picks an explicit GPU target device when the active policy wants GPU ingest
- attempts direct safetensors reads on the requested device
- falls back to CPU read + tensor-by-tensor copy if direct GPU safetensors loading fails
- still uses CPU-first
torch.load()for pickle formats (.ckpt,.pt,.pth,.bin) - records the actual load method in the residency registry
For safetensors loads happening inside a model / clip / vae context, it can select only the detected component keys from the file header instead of pulling the full file into memory first.
2) Loader contexts are attached to stock ComfyUI load paths
These stock paths are wrapped with registry context and output binding:
- checkpoint loads
- diffusion-model loads
- CLIP loads
- CLIP Vision loads
- ControlNet loads
- diffusers loads
That means the native registry is not limited to the custom resident nodes. Stock ComfyUI loaders that pass through these paths are also tracked.
3) Compatible external caches can join the residency system lazily
When a compatible SeedVR2 cache module is present, the loader installs cache-level hooks that register SeedVR2-owned cached DiT / VAE objects into a separate external registry.
Those entries:
- are refreshed from the live cached object at runtime
- appear in Registry Snapshot output under
external_entries - are evicted through SeedVR2’s own cache-removal methods rather than the normal Comfy unload path
- remain outside automatic trim unless
COMFYUI_GPU_RESIDENT_TRIM_EXTERNAL=1is set
4) Device/offload policy is overridden
Depending on the active policy, the patcher can steer:
- initial UNet load device
- CLIP/Text Encoder device
- VAE device
- offload devices for UNet / CLIP / VAE
This is how prefer_gpu and sticky_gpu keep more of the hot path on the GPU side than stock ComfyUI would.
5) free_memory() becomes sticky-aware
Under sticky_gpu, comfy.model_management.free_memory() is patched so that:
- sticky tracked wrappers are considered first
- higher-priority sticky entries are protected first
- lower-priority or older sticky entries yield first when VRAM must be reclaimed
- a transient protection floor is applied so ComfyUI does not immediately tear down high-value resident entries for small requests
6) Clone replacement is hardened
load_models_gpu() is patched to fully unload clone-conflict wrappers before replacement instead of relying on a shallow detach path that can leave base weights patched.
7) Unload / detach is redirected to CPU when needed
LoadedModel.model_unload() and ModelPatcher.detach() are patched so that unloads which would otherwise not reclaim VRAM are redirected through a CPU offload target first.
8) VAE encode/decode gets a sticky-safe path
Under sticky_gpu, patched VAE.encode() and VAE.decode():
- cap the working batch count when necessary to preserve transient VRAM headroom
- retry with tiled VAE encode/decode on OOM
That behavior is not a general performance feature toggle. It exists to reduce avoidable VRAM spikes while sticky residency is active.
Policies
The registry exposes four global policies:
legacy
Stay closest to stock ComfyUI behavior. Registry tracking still exists, but the patcher does not aggressively steer ingest/offload toward the GPU path.
balanced
Keep registry tracking and diagnostics without aggressive GPU residency behavior.
prefer_gpu
Prefer GPU ingest for tracked model-like loads and keep the faster side of the device/offload policy for:
- diffusion models
- CLIP / text encoders
- ControlNets
This policy does not auto-pin tracked objects.
sticky_gpu
Builds on prefer_gpu and additionally:
- auto-pins newly bound models and CLIPs
- keeps VAE offload on the GPU side as well
- patches
free_memory()to protect sticky tracked wrappers by priority - uses the sticky-safe VAE encode/decode behavior
Default policy selection
Selection order is:
COMFYUI_GPU_RESIDENT_POLICY, if set to a supported valuesticky_gpuwhen ComfyUI is started with--gpu_onlysticky_gpuwhen ComfyUI is started with--highvram- otherwise
prefer_gpu
Supported values are:
legacybalancedprefer_gpusticky_gpu
Included nodes
All nodes live under the GPU Resident Loader category.
Loader nodes
Diffusion Model Selector Resident
Returns an absolute path string for a selected diffusion model.
Notes:
- resolves from
diffusion_models - also exposes
text_encodersentries whose filename containsconnector
Diffusion Model Loader Resident
KJ-style diffusion-model loader with these controls:
weight_dtypecompute_dtypepatch_cublaslinearsage_attentionenable_fp16_accumulation- optional
extra_state_dict - optional
policy_override
Behavior:
- for
.safetensors, it loads only the detected UNet portion of the file - if
extra_state_dictis provided, only matching UNet keys are merged - repeated loads reuse a live equivalent model when the source path and loader-relevant options still match
- before GPU-bound loads, it estimates the upcoming footprint and trims only enough lower-priority residency to cover the request plus adaptive headroom
Checkpoint Loader Resident
Full checkpoint loader that returns:
MODELCLIPVAE
Behavior:
- shares the same tuning knobs as the resident diffusion-model loader for the model component
- reuses already-live equivalent components when possible
- composes the final output from model / clip / vae component loaders instead of always rebuilding the whole checkpoint path from scratch
Checkpoint Model Loader Resident
Model-only checkpoint loader.
Behavior:
- takes the same selective safetensors UNet fast path as the diffusion-model loader
- reuses a live equivalent model when available
- uses the same dtype / attention / cublas / fp16-accumulation knobs as the full checkpoint loader
Checkpoint Clip Loader Resident
CLIP-only checkpoint loader.
Behavior:
- can reuse a live equivalent CLIP object
- avoids rebuilding the diffusion model and VAE outputs when only CLIP is needed
Checkpoint VAE Loader Resident
VAE-only checkpoint loader.
Behavior:
- can reuse a live equivalent VAE object
- avoids rebuilding the diffusion model and CLIP outputs when only VAE is needed
Residency nodes
Set Global Residency Policy
Sets the active global policy and returns it as a STRING.
The loader nodes also expose an optional policy_override string input for one-off loads.
Registry Snapshot
Returns a composite formatted JSON snapshot with:
policyfor the active global policyentriesfor native Comfy-managed registry entriesexternal_entriesfor compatible external cache entries discovered at runtime
Pin Model Residency / Pin CLIP Residency / Pin VAE Residency
Marks a tracked native object as sticky or non-sticky and optionally changes its priority.
Preload Model To GPU / Preload CLIP To GPU / Preload VAE To GPU
Calls load_models_gpu(..., force_full_load=True) for the selected native object, then updates sticky state / priority in the registry.
Evict Model From GPU / Evict CLIP From GPU / Evict VAE From GPU
Attempts to unload the selected native object from the current loaded-model set.
unpatch_weights=True performs a full unload path. When eviction succeeds, the node returns evicted; otherwise not_loaded.
Report Model Residency / Report CLIP Residency / Report VAE Residency
Returns a JSON report for a single tracked native object.
If the object is not currently bound in the registry, the node returns a JSON payload with tracked: false.
Adaptive trimming before resident loads
The resident loaders now do load-scoped VRAM trimming themselves.
Before a GPU-bound resident load, the loader estimates required bytes from:
- the safetensors header when possible
- the detected checkpoint component subset when possible
- otherwise the source file size as a fallback
It then requests enough free VRAM for:
- the estimated load size
- adaptive headroom
Current adaptive headroom policy:
- ratio:
12.5%of the estimated load - floor:
256 MiB - ceiling:
1 GiB
The trim path prefers to:
- unload non-sticky entries first
- then lower-priority sticky entries
- preserve explicitly kept models
- use partial unload where available
- include compatible external cache entries in the same candidate search only when
COMFYUI_GPU_RESIDENT_TRIM_EXTERNAL=1, they are visible, on the same device, and not currently claimed/in use
This logic lives in the resident loader path. You do not need a separate “target free VRAM” node for it.
Registry and observability
The snapshot now exposes two collections:
entriesfor native Comfy-managed tracked objectsexternal_entriesfor compatible external cache objects
Native registry entries
The native registry tracks residency metadata for bound objects.
Typical per-entry fields include:
entry_idkindsource_pathbasenamestickyprioritycreated_atlast_touchedloaded_bytestotal_bytesload_deviceoffload_devicecurrent_devicelast_methodlast_reportloader_keynotesalive
The last_method / last_report fields let you see whether a load actually used:
- direct safetensors GPU ingest
- safetensors CPU -> CUDA fallback
- safetensors component-only load
- CPU-first
torch.load()compatibility path - a recorded load failure
External registry entries
Typical external-entry fields include:
entry_idcache_keykindsource_pathbasenamestickyprioritycreated_atlast_touchedloaded_bytestotal_bytesload_deviceoffload_devicecurrent_deviceclaimednotesaliveexternal
For SeedVR2-backed entries, claimed: true means the cache object is currently marked in use and is skipped by the external trim candidate search when that opt-in path is enabled.
What gets tracked
Native tracked/bound paths
Native tracked paths include:
- resident node loads from this repo
- stock checkpoint loads
- stock diffusion-model loads
- stock CLIP loads
- stock CLIP Vision loads
- stock diffusers loads
ControlNet loads also participate in the patched load context and device-policy path, but this repo does not currently expose dedicated ControlNet residency nodes.
External tracked paths
Current external integration coverage is:
- SeedVR2 global cached DiT entries
- SeedVR2 global cached VAE entries
Those entries are discovered lazily from compatible SeedVR2 cache modules at runtime. They are tracked separately from the native registry and participate in snapshot plus provider-specific eviction decisions, with load-scoped trim available only through the explicit external-trim opt-in.
Important limits and non-goals
Best path is still .safetensors
The narrow fast path is built around .safetensors.
That is where this repo can:
- inspect headers cheaply
- select only model / clip / vae subsets
- estimate component bytes more accurately
- attempt direct device-targeted reads
.ckpt / .pt / pickle formats are still CPU-first
For pickle-based formats, PyTorch still goes through torch.load() on CPU first.
The repo can still:
- track those loads
- keep the resulting live objects resident
- reuse equivalent live objects later
It does not claim direct-to-GPU ingest for those formats.
Cross-process persistence is out of scope
This repo does not keep VRAM allocations alive after ComfyUI, Python, or WSL exits.
CUDA memory lifetime is process/context scoped. True persistence across process shutdown would need a separate long-lived keeper process or service that owns the CUDA context.
External integrations are compatibility-based, not universal
The external registry does not automatically manage every third-party cache.
At the moment, the documented external integration target is SeedVR2. Other custom nodes with private caches remain invisible until this repo grows a provider-specific integration for them.
It does not automatically capture arbitrary custom loader implementations
The native registry only sees objects that pass through the patched ComfyUI load paths or through this repo’s resident nodes.
If another custom node loads models through its own private code path and bypasses those patched entry points, that object may never become a tracked native registry entry. In that case, the preload / pin / evict / report nodes from this repo cannot manage it until that external loader is integrated or patched.
Likewise, even for supported external providers such as SeedVR2, the current external integration is about observation + provider-specific eviction, with automatic trim kept opt-in. This repo does not yet expose dedicated external preload / pin / report / evict nodes for provider-owned cache entries.
Installation
Clone into custom_nodes:
git clone https://github.com/xmarre/ComfyUI-GPU-Resident-Loader ComfyUI/custom_nodes/ComfyUI-GPU-Resident-Loader
Install dependencies into the same Python environment ComfyUI uses:
pip install -r ComfyUI/custom_nodes/ComfyUI-GPU-Resident-Loader/requirements.txt
Requirements declared by the repo:
- Python
>=3.10 safetensors>=0.4.3
Optional SageAttention dependencies are not installed by default. Install those separately if you plan to use a SageAttention mode in the resident loaders.
Basic usage patterns
1) Large-VRAM, mostly resident workflow
Recommended baseline:
- start ComfyUI with
--highvramor set policy manually tosticky_gpu - prefer
.safetensorsfor hot models - load diffusion models through Diffusion Model Loader Resident
- use Preload ... To GPU for models you know you will reuse
- inspect with Report ... Residency or Registry Snapshot
2) Full checkpoint workflow
Use Checkpoint Loader Resident when you want MODEL + CLIP + VAE together.
That path can reuse already-live components instead of always rebuilding all three outputs.
3) Staged checkpoint workflow
Use component loaders when the graph does not need the whole checkpoint at once:
- Checkpoint Model Loader Resident for diffusion model only
- Checkpoint Clip Loader Resident for CLIP only
- Checkpoint VAE Loader Resident for VAE only
4) Manual native residency control
Use:
- Pin ... Residency to mark a tracked native entry sticky / non-sticky
- Preload ... To GPU to force a full live native load now
- Evict ... From GPU to unload it from the current native loaded-model set
5) Mixed workflows with SeedVR2 external caching
If SeedVR2 keeps DiT or VAE models in its own global cache, those objects can now show up in Registry Snapshot under external_entries.
That means:
- you can see that those bytes exist even though they are outside
current_loaded_models - resident loader trim only reclaims them when
COMFYUI_GPU_RESIDENT_TRIM_EXTERNAL=1, they are visible, on the same device, and not currently claimed/in use - eviction goes through SeedVR2’s own cache-removal path instead of a normal Comfy wrapper unload
Notes on compatibility and migration
Legacy wiring: extra_state_dict used as a policy string
The resident diffusion-model loader contains a compatibility shim for older graphs:
- if
extra_state_dictreceives one of the known policy names - and that value is not an existing file path
- it is interpreted as
policy_overrideinstead
New graphs should connect policy strings to policy_override, not to extra_state_dict.
Convert hot pickle checkpoints to safetensors
scripts/convert_checkpoint_to_safetensors.py is included for one-time conversion of hot .ckpt / .pt / .pth style checkpoints.
Example:
python ComfyUI/custom_nodes/ComfyUI-GPU-Resident-Loader/scripts/convert_checkpoint_to_safetensors.py \
--input /path/to/model.ckpt \
--output /path/to/model.safetensors
Optional flags:
--state-dict-key <key>to extract a different top-level dict key--allow-non-tensor-valuesto skip non-tensor entries instead of failing
License
GPL-3.0-or-later.
This repo stays GPL-compatible because it adapts behavior from GPL-licensed ComfyUI and mirrors relevant loader behavior from GPL-3.0-licensed KJNodes.
Run ComfyUI workflows without the setup
No installs, no CUDA version roulette, no GPU sitting idle on your bill. Bring a workflow and run it in the browser.