How ComfyUI Graphs Execute and Where VRAM Goes
Node graph execution order, caching, VRAM pressure, and custom node dependency management in ComfyUI.
ComfyUI exposes a diffusion pipeline as an explicit node graph instead of a form with a prompt box. That trade is deliberate: you give up a simple interface and get direct control over every stage, plus the ability to inspect and reuse intermediate results. Understanding how the graph actually executes is what turns it from a puzzle into a tool.
The graph is pulled, not pushed
When you queue a run, execution starts from the output nodes and walks backwards to find everything they depend on. Only nodes that feed an output get executed. A node sitting in the canvas that nothing consumes is skipped entirely, which is why a change you made can appear to do nothing at all.
Results are cached per node based on that node’s inputs. Change a value late in the graph and only the nodes downstream of the change rerun. This is the single biggest workflow speedup available: put expensive, stable work early, and iterate on cheap parameters near the end. Reloading a checkpoint on every run because you touched something upstream of the loader wastes most of your iteration time.
The stages a basic image graph contains
A minimal text to image graph has a recognizable shape. A checkpoint loader brings in the model weights, which usually split into three outputs: the diffusion model itself, the text encoder, and the decoder that turns latents back into pixels.
Text prompts go through the text encoder to become conditioning. Positive and negative conditioning are separate inputs, not one field with syntax. An empty latent node defines the working resolution. The sampler takes model, both conditionings, and a latent, then runs the denoising loop. Finally the decoder converts the resulting latent into an image, and a save or preview node writes it out.
Everything more elaborate is a variation. Image to image swaps the empty latent for an encoded existing image and reduces the denoise strength. Upscaling chains a second sampler pass at a higher latent resolution. Control adapters and additional weights inject into the conditioning or the model before the sampler.
VRAM is the real constraint
Memory pressure comes from three places at once: the resident model weights, the activation memory of the denoising loop, and the decode step. The decode is often the surprise, because it can peak higher than the sampling that preceded it, which is why a run sometimes fails at the very end after appearing to work.
Higher resolutions raise activation memory faster than they raise anything else. Batch size multiplies it directly. If you are close to the limit, generate one image at a time and upscale in a second pass rather than sampling at full target resolution in one shot. Tiled decoding trades speed for a lower peak when the decode is what tips you over.
Loading several large models in one graph is the other common cause. Each additional loader is another set of weights that has to be resident or swapped. Keeping model variety low within a single workflow is cheaper than any clever memory setting.
Custom nodes are your main operational risk
The custom node ecosystem is where most capability lives and where most breakage comes from. Custom nodes install Python dependencies into the same environment as the core application, so two packs that want incompatible versions of a shared library will fight, and the loser usually manifests as nodes silently failing to load at startup.
Treat the environment as something you can rebuild. Use an isolated virtual environment, record what you installed, and read the startup console output, because that is where import failures are reported. When a workflow you downloaded refuses to load, the cause is almost always a missing custom node rather than a corrupt file.
Keep a known good workflow saved separately as a baseline. When something breaks, running it tells you immediately whether the problem is the environment or the graph you were editing.