The ComfyUI LoRA loader node explained in one line: LoraLoader takes the MODEL and CLIP outputs of your checkpoint, reads a LoRA file from models/loras, and returns patched clones of both, scaled by strength_model and strength_clip (default 1.0, allowed range -100 to 100). Add one node per LoRA and chain them in series.
Nothing gets merged into the checkpoint on disk, and base weights are not copied. Most “my LoRA does nothing” reports trace to three causes: bad wiring, a mismatched base architecture, or a misread strength value.
What does the LoRA loader node actually do?
It attaches weight patches to a clone of the model. In the ComfyUI source, load_lora resolves the file under the loras folder, loads it with safe_load=True, caches it on the node instance, and hands it to comfy.sd.load_lora_for_models, which maps the file’s keys onto the diffusion model and text encoder, then calls add_patches on a clone of each.
Three operational details:
- Clones share weights.
ModelPatcher.clone()copies the patch dictionary but points at the same underlying model object. Chaining three loaders does not hold three copies of an SDXL UNet. - Patches are applied at load time. When the model moves to the GPU, ComfyUI backs up each original weight to the offload device and writes the patched value in its place. For layers left offloaded in low-VRAM mode, a
LowVramPatchcomputes the patched weight during execution instead, which avoids materialising everything at once. - Both strengths at 0 is a true no-op. The first line of
load_lorareturns the inputs untouched whenstrength_model == 0 and strength_clip == 0, skipping the file read and patching.
Per the built-in node docs, the node also finds files in subfolders of ComfyUI/models/loras. The ComfyUI examples page says LyCORIS, LoHa, LoKr and LoCon files load through the same node.
What do strength_model and strength_clip scale?
They multiply the learned weight delta before it is added to the frozen base weight: strength_model for the diffusion model, strength_clip for the text encoder. For a standard LoRA file, ComfyUI’s weight adapter computes:
W' = W + s * (alpha / rank) * (up @ down)
s = strength_model or strength_clip
alpha = scalar stored in the LoRA file
rank = first dimension of the down matrix
That is the update from the original LoRA paper (Hu et al., 2021): pretrained weights stay frozen, and each adapted layer gets a pair of trainable low-rank matrices. ComfyUI’s slider is the outer scalar.
So 1.0 is not 100% of a fixed amount. It means “the delta at the scale the trainer chose through alpha”, and two files at 1.0 can differ widely, so calibrate strength per file. The official node docs put the typical range at 0 to 1. Negative values subtract the delta, letting “slider” LoRAs push in both directions.
Two fields exist because, per the ComfyUI examples FAQ, the CLIP and MODEL parts of a LoRA “will most likely have learned different concepts”. A single 0.8 in another UI equals 0.8 in both fields here. ComfyUI’s author, in GitHub discussion #215: if you don’t know, set them to the same value.
Some LoRAs are trained only on the diffusion model; their files have no text-encoder keys, so strength_clip has no effect.
Load LoRA vs Load LoRA (Model and CLIP): which one?
Use LoraLoaderModelOnly when the LoRA has no text-encoder weights or you want the text encoder left alone, and LoraLoader otherwise. The model-only class is a thin wrapper: it calls load_lora(model, None, lora_name, strength_model, 0) and returns only the MODEL.
Current source displays LoraLoader as “Load LoRA (Model and CLIP)” and LoraLoaderModelOnly as “Load LoRA”, so an older screenshot titled “Load LoRA” may show either node. Saved workflows record the stable class names; ComfyUI workflow JSON vs PNG metadata covers that serialisation.
Wiring it up
The chain runs checkpoint, LoRA, LoRA, then the encoders and sampler. Every node that consumes MODEL or CLIP must read from the last loader. This complete SDXL graph chains two loaders, in the API format ComfyUI’s /prompt endpoint accepts:
{
"4": {
"class_type": "CheckpointLoaderSimple",
"inputs": { "ckpt_name": "sd_xl_base_1.0.safetensors" }
},
"10": {
"class_type": "LoraLoader",
"inputs": {
"model": ["4", 0],
"clip": ["4", 1],
"lora_name": "style/ink_wash.safetensors",
"strength_model": 0.8,
"strength_clip": 0.8
}
},
"11": {
"class_type": "LoraLoader",
"inputs": {
"model": ["10", 0],
"clip": ["10", 1],
"lora_name": "characters/knight.safetensors",
"strength_model": 0.6,
"strength_clip": 1.0
}
},
"6": {
"class_type": "CLIPTextEncode",
"inputs": { "clip": ["11", 1], "text": "armored knight, ink wash painting, misty mountains" }
},
"7": {
"class_type": "CLIPTextEncode",
"inputs": { "clip": ["11", 1], "text": "blurry, lowres" }
},
"5": {
"class_type": "EmptyLatentImage",
"inputs": { "width": 1024, "height": 1024, "batch_size": 1 }
},
"3": {
"class_type": "KSampler",
"inputs": {
"model": ["11", 0],
"positive": ["6", 0],
"negative": ["7", 0],
"latent_image": ["5", 0],
"seed": 42,
"steps": 25,
"cfg": 7.0,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1.0
}
},
"8": {
"class_type": "VAEDecode",
"inputs": { "samples": ["3", 0], "vae": ["4", 2] }
},
"9": {
"class_type": "SaveImage",
"inputs": { "images": ["8", 0], "filename_prefix": "lora_chain" }
}
}
Node 11 feeds both text encoders and the sampler. If nodes 6 and 7 still read ["4", 1], the CLIP half of both LoRAs is silently dropped. If node 3 reads ["4", 0], neither loader runs. ComfyUI executes backwards from the output nodes and skips any node nothing consumes, as covered in how ComfyUI graphs execute and where VRAM goes.
To calibrate a new file, sweep its strength on a fixed seed rather than eyeballing single renders. This script follows the repo’s basic API example and queues one job per strength:
import copy
import json
from urllib import request
COMFY_URL = "http://127.0.0.1:8188/prompt"
with open("lora_chain_api.json") as f:
base = json.load(f)
for s in (0.0, 0.4, 0.6, 0.8, 1.0, 1.2):
wf = copy.deepcopy(base)
wf["10"]["inputs"]["strength_model"] = s
wf["10"]["inputs"]["strength_clip"] = s
wf["9"]["inputs"]["filename_prefix"] = f"sweep_ink_s{s:.1f}"
payload = json.dumps({"prompt": wf}).encode("utf-8")
request.urlopen(request.Request(COMFY_URL, data=payload))
The 0.0 entry hits the no-op path, giving a free control image without the first LoRA. Keep seed, prompt and sampler settings fixed, the same discipline as a golden set in model regression testing, which SentryML covers from the monitoring side.
With an existing Automatic1111 install, point ComfyUI at its LoRA folders rather than duplicating files. extra_model_paths.yaml accepts multi-line folder lists, per the shipped example file:
a1111_shared:
base_path: /mnt/models/stable-diffusion-webui/
loras: |
models/Lora
models/LyCORIS
What you’ll see
When a LoRA works, the sweep changes steadily in one direction. As strength rises, the style or subject strengthens until it starts overriding the prompt. The console stays quiet at load.
Failure patterns are easy to recognise:
- Every frame is identical. The loader is not in the execution path. Check which node the sampler’s
modelinput reads from. - The console fills with
lora key not loaded:lines.comfy/lora.pylogs one for every file key that doesn’t map onto the loaded model, usually due to an architecture mismatch such as an SD 1.5 LoRA on an SDXL checkpoint. The run still completes with partial or no effect, so the console is your only signal. - Textures burn, anatomy warps or colours blow out at high strength. The delta is overpowering the base weights. Lower the strength or remove one LoRA from the stack.
- The style shows up but the trigger word does nothing. Either the CLIP is wired from the checkpoint, or
strength_clipis 0.
Caveats
Stacking is additive. Each standard LoRA adds its own delta to the same base weight. Two files at 1.0 that touch the same layers push roughly twice as hard as either alone, so lower each strength when combining them. For plain LoRA the sum doesn’t depend on order. DoRA files go through a separate weight-decomposition path in ComfyUI, where chain order can change the result.
Each node caches one file. Changing strength reuses the cached file, while changing lora_name reads from disk again. Many loaders means many state dicts in system RAM, and any strength change still forces a re-patch and re-sample downstream.
Patching is not free. Backups of patched weights stay on the offload device so ComfyUI can unpatch later, and low-VRAM recomputation during execution is slower. When VRAM is tight, see ComfyUI out of memory errors. The experimental LoraLoaderBypass node adds the LoRA’s contribution in the forward pass without modifying base weights, which its docs call useful when weights are offloaded.
Prompt tags do nothing in core. Typing <lora:name:0.8> into a CLIP Text Encode node gives plain prompt text. GitHub discussion #1686 confirms no LoRA gets loaded. Tag-parsing loaders are custom nodes, installable through ComfyUI Manager. ComfyUI vs Automatic1111 vs Forge covers this among other practical differences.
Masking and scheduling need other nodes. LoraLoader applies one strength everywhere at every step. The hook nodes the Comfy blog announced December 6, 2024, such as Create Hook LoRA, can mask a LoRA to regions and schedule its strength across steps. The post notes each keyframe adds recalculation during sampling.
Treat downloaded files as untrusted. For non-safetensors formats, load_torch_file calls torch.load with weights_only=True, which blocks arbitrary pickled objects. Prefer .safetensors anyway.
FAQ
where do i put lora files in comfyui
Put LoRA files in ComfyUI/models/loras. The loader’s dropdown lists every file there, including those in subfolders, and the subfolder name becomes part of the lora_name value. To reuse an Automatic1111 library, add its models/Lora and models/LyCORIS folders under a loras: key in extra_model_paths.yaml instead of copying files.
why is my lora not doing anything in comfyui
Usually the LoRA node is outside the execution path: the KSampler’s model input, or the text encoders’ clip input, still reads from the checkpoint loader, not the last LoRA loader. If the wiring is right, lora key not loaded lines in the console usually mean the file was trained for a different base architecture.
what strength should i use for a lora in comfyui
Start at the default 1.0 in both fields, then sweep downward on a fixed seed. The official node docs put typical values between 0 and 1. Effective scale also depends on each file’s alpha and rank, so the right number differs per LoRA. Lower each strength when stacking several files.
can i use sd 1.5 loras with sdxl in comfyui
No. A LoRA only works on the architecture family it was trained against, because its keys name specific layers in that model. With an SD 1.5 LoRA on an SDXL checkpoint, most keys fail to map; ComfyUI logs lora key not loaded for each, and the image shows little or no LoRA effect.
Related across the network
- LLM Fine Tuning in Production: A Practical MLOps Guide — sentryml.com
- How Much VRAM Do You Need to Run Local LLMs with Ollama? — ollamalab.com