self-hosted/ai
§01·recipe · image

Qwen-Image-2.1 on RTX 3060: int8 text-to-image and editing in ComfyUI (Ampere sm_86, 12 GB)

imageintermediate12GB+ VRAMSep 21, 2026

This intermediate recipe sets up Qwen-Image-2.1 on the RTX 3060, needing about 12 GB of VRAM.

models
tools
prerequisites
  • NVIDIA RTX 3060 12 GB (Ampere GA106, compute capability 8.6)
  • ComfyUI v0.37.0 or newer: the v0.37.0 Windows portable (the nvidia build, not the cu126 one) or a git install
  • A CUDA 13 (cu130) build of PyTorch and an NVIDIA driver from the 580 branch or newer
  • Python 3.10 or newer for a git install (the portable ships 3.13)
  • 32 GB system RAM recommended: the three weight files are 16.096 GiB and whatever is not on the card has to live in RAM
  • 18 GB free disk for the int8 diffusion model, int8 text encoder and VAE (17.28 GB as Hugging Face counts it)
  • Research or evaluation use only: the Qwen Research License forbids commercial use of the weights

What You'll Build

A local Qwen-Image-2.1 setup on a 12 GB RTX 3060 in ComfyUI, using the same int8 weights ComfyUI's own templates load: text-to-image at 1024×1024 and at the model's native 2048×2048, RGBA output with a real alpha channel, and instruction-based editing with reference images. Qwen-Image-2.1 is a 7B single-stream diffusion transformer (32 layers, per the repo's transformer/config.json) behind a Qwen3-VL-8B text encoder and a 64-channel RGBA VAE, released 2026-09-20. On this card the transformer is not the hard part — 6.758 GiB in int8. The text encoder is: at 8.709 GiB it is the larger file, and its stage is the one that comes closest to 12 GiB.

⚠️ Read the licence before you download 17 GB. The weights ship under the Qwen Research License Agreement. Section 1.i defines "Non-Commercial" as the only permitted use — it "shall mean for research or evaluation purposes only." — and section 2.a grants the licence "FOR NON-COMMERCIAL PURPOSES ONLY". The grant is worldwide and there is no clause excluding any territory (section 4.a only reminds you that export controls may apply); the restriction is on purpose, not on place. Section 4.b reaches outputs in one specific case: "If you use the Materials or any outputs or results therefrom to create, train, fine-tune, or improve an AI model that is distributed or made available, you shall prominently display “Built with Qwen” or “Improved using Qwen” in the related product documentation." Every repack on this page — Comfy-Org's int8 files included — is a derivative and carries the same terms. Section 2.b gives an address for a commercial licence; a reader reports in the vendor's discussion #7 that mail to it bounces. This is a reader's summary of the text, not legal advice.

Hardware data: RTX 3060 (12 GB VRAM) · int8 DiT 6.758 GiB + int8 text encoder 8.709 GiB — 15.467 GiB together, so never both fully resident · See benchmark data

ℹ️ Nothing on this page was measured on an RTX 3060. No run of this model on a 12 GB card, or on any Ampere card, turned up on the surfaces searched for this page on 2026-09-21: every comment in the Hacker News launch thread, every comment in every discussion on the vendor's Hugging Face repo and on the Comfy-Org repack, the vendor's GitHub issues, and the 100 newest issues and pull requests on the ComfyUI tracker. Reddit could not be read from the session that wrote this page. Every VRAM figure below is derived from byte counts and from ComfyUI's own source at tag v0.37.0, and the arithmetic is shown so you can check it. If you run it, post your numbers.

Requirements

ComponentMinimumThis recipe
GPU12 GB VRAM, NVIDIARTX 3060 (12 GB) — not measured; the budget below is derived (/contribute)
RAM32 GB recommended— the three files are 16.096 GiB, and what is not on the card lives in RAM
Storage17,283,091,112 B = 16.096 GiB (int8 DiT + int8 encoder + VAE)— byte counts from the Comfy-Org tree API
SoftwareComfyUI ≥ v0.37.0, PyTorch cu130, driver ≥ 580

The RTX 3060 is Ampere, compute capability 8.6 in NVIDIA's own table. The whole page turns on that number: Ampere has INT8 tensor cores and no FP8 or FP4 ones, and ComfyUI decides per device which weight formats run natively from exactly that.

The three files the official templates load, byte-exact from the Comfy-Org repack's tree API (commit ace0edeb37, read 2026-09-21 — this is launch week, re-check the sizes before you rely on them):

FileBytesGiBDestination
diffusion_models/qwen_image_2.1_int8_convrot.safetensors7,256,783,0646.758models/diffusion_models/
text_encoders/qwen3vl_8b_int8_convrot.safetensors9,350,798,3608.709models/text_encoders/
vae/qwen_image_2.1_vae_bf16.safetensors675,509,6880.629models/vae/

The "Staged" figures in the RX 9070 XT log quoted in issue #16437 — int8 DiT 6920 MB, int8 encoder 8916 MB, VAE 644 MB — are these same file sizes in MiB. They show that staging does not inflate the weights; they are not peak VRAM readings.

The same repo holds a bf16 DiT (14,230,280,616 B = 13.253 GiB — past this card on its own), a bf16 encoder (17,534,334,616 B = 16.330 GiB), a w4a8 encoder (6,312,105,364 B = 5.879 GiB — the one alternative that matters here), and two qwen3.5_9b_…_pe_… files, which are prompt enhancers, not encoders. The repo's recursive tree lists ten files, and its two DiT builds are bf16 and int8; there is no fp8 or nvfp4 DiT in it, nor any Qwen-Image-2.1 file in the other five Comfy-Org/Qwen-Image* repos. The ComfyUI docs page for this model still lists the bf16 encoder as the one to download; the templates bundled with v0.37.0 load the int8 one, and on this card the bf16 encoder cannot be resident at all, so follow the templates.

What actually gates this card

ComfyUI has two loaders, and they budget VRAM differently. On this card the default is DynamicVRAM: main.py at v0.37.0 enables it on any NVIDIA device once PyTorch is 2.8 or newer and comfy-aimdo initialises. You land on the legacy estimate-based loader instead if you pass --disable-dynamic-vram (or --highvram, --gpu-only, --novram, --cpu), or if the startup log says Falling back to legacy ModelPatcher — PyTorch older than 2.8, or no working comfy-aimdo. Both views below come from ComfyUI's source at tag v0.37.0 (comfy/model_management.py, comfy/model_base.py, comfy/sd.py, comfy/model_patcher.py), from comfy-aimdo v0.5.5 (the version requirements.txt pins), and from the byte counts above.

Two estimates feed both views, and both are ComfyUI's planning figures, not peaks anyone read off nvidia-smi:

  • The sampler's activations. area × dtype_size × 0.01 × memory_usage_factor MiB, with memory_usage_factor = 6.0 for this model in comfy/supported_models.py and area the latent grid: 64×64 = 4,096 tokens at 1024², 128×128 = 16,384 at 2048², because the VAE downsamples 16× and the transformer uses patch size 1. In bf16 that is 0.480 GiB and 1.920 GiB. The source carries a TODO saying the formula needs tweaking.
  • The VAE decode. For this VAE comfy/sd.py estimates 900 × h × w × 256 × dtype_size bytes over the latent grid: 1.758 GiB at 1024², 7.031 GiB at 2048².

DynamicVRAM (the default). ComfyUI's own reserve plays no part here. load_models_gpu() still computes it and passes it down, but ModelPatcherDynamic.partially_load() accepts that extra_memory argument and never uses it. The floor is comfy-aimdo's, and it differs by OS. On Linux, src/plat.h defines VRAM_HEADROOM (256 * 1024 * 1024) — 0.25 GiB — and keeps that much free against the card's live free VRAM, i.e. after whatever the desktop and the CUDA context already hold. The Windows build uses src-win/shmem-detect.c instead, which keeps 512 MiB — 0.5 GiB — free against both the WDDM budget and NVML's free-VRAM reading (96 MiB if it has to fall back to cuMemGetInfo). Its budget takes the larger of that live check and a second one against the card's total capacity, then adds any extra headroom. --reserve-vram N feeds only the second, total-capacity check — comfy-aimdo's own control.py says raising it above 256 MB is honoured but lowering it below changes nothing — and --vram-headroom N adds N GiB on top of both. Weights are loaded on demand and evicted when an allocation needs the room — the comfy-aimdo README describes "on-demand offloading of model weights when the primary pytorch VRAM allocator comes under pressure". So a stage never fails for want of room for its weights; what the numbers below tell you is whether its weights can stay resident for the whole stage, or have to be evicted and re-read part-way.

Stage (DynamicVRAM)Weights+ ComfyUI's estimate+ headroomStage totalAgainst 12 GiB
Text encode, int8 encoder8.709 GiB— (none made)0.25 GiB8.959 GiB3.041 GiB spare
Text encode, w4a8 encoder5.879 GiB0.25 GiB6.129 GiB5.871 GiB spare
Denoise at 1024²6.758 GiB0.480 GiB0.25 GiB7.488 GiB4.512 GiB spare
Denoise at 2048²6.758 GiB1.920 GiB0.25 GiB8.928 GiB3.072 GiB spare
VAE decode at 1024²0.629 GiB1.758 GiB0.25 GiB2.637 GiB9.363 GiB spare
VAE decode at 2048²0.629 GiB7.031 GiB0.25 GiB7.910 GiB4.090 GiB spare — the DiT's 6.758 GiB does not fit beside it

The table uses the Linux headroom. On Windows the headroom is 0.5 GiB, so every stage total is 0.25 GiB higher and every spare figure 0.25 GiB lower: the encoder stage becomes 9.209 GiB, with 2.791 GiB spare.

Legacy loader (only if you are on it). Here the reserve is real: minimum_inference_memory() is 0.8 GiB plus EXTRA_RESERVED_VRAM, which is 400 MiB (0.391 GiB) on Linux — 1.191 GiB. On Windows it is 600 MiB, and a further 100 MiB is added only when the card exceeds 15 GiB, so a 12 GB card on Windows gets 1.386 GiB. The loader keeps the larger of that reserve and the stage's estimate plus EXTRA_RESERVED_VRAM free, and loads the model whole only if what remains holds it — otherwise it loads it partially. The totals are its residency thresholds:

Stage (legacy loader, Linux)WeightsWhat the loader keeps freeStage totalAgainst 12 GiB
Text encode, int8 encoder8.709 GiB1.191 GiB (the reserve)9.899 GiB2.101 GiB spare
Text encode, w4a8 encoder5.879 GiB1.191 GiB7.069 GiB4.931 GiB spare
Denoise at 1024²6.758 GiB1.191 GiB (0.480 + 0.391 is below the reserve)7.949 GiB4.051 GiB spare
Denoise at 2048²6.758 GiB2.311 GiB (1.920 + 0.391)9.069 GiB2.931 GiB spare
VAE decode at 1024²0.629 GiB2.149 GiB (1.758 + 0.391)2.778 GiB9.222 GiB spare
VAE decode at 2048²0.629 GiB7.422 GiB (7.031 + 0.391)8.051 GiB3.949 GiB spare — only with the DiT off the card

On Windows the larger reserve turns the encoder stage's 2.101 GiB into 1.905 GiB.

The "spare" column in both tables is against the full 12 GiB. The desktop's share and the CUDA context come out of it under either loader: both read live free VRAM when they decide what stays resident, which is why neither needs a flag for them, but neither leaves them outside the 12 GiB either. 3.041 GiB (DynamicVRAM on Linux; 2.791 GiB on Windows) or 2.101 GiB (legacy; 1.905 GiB on Windows) holds a desktop and a CUDA context comfortably; the thin margins further down do not.

Four things follow, in either view.

The encoder and the DiT never sit on the card whole at the same time. 6.758 + 8.709 = 15.467 GiB, and even with the w4a8 encoder it is 12.637 GiB — past 12 GiB before any headroom. Under DynamicVRAM ComfyUI does not unload one model to make room for the other — the comment in free_memory() says "don't actually unload dynamic models for the sake of other dynamic models" — and comfy-aimdo evicts weights as allocations need the room. The practical consequence is an inference from the arithmetic, not a measurement: a new prompt re-runs the encoder, so its weights have to come back onto the card, and the DiT's have to come back when sampling starts. One tier up, on a 16 GB RX 9070 XT, the reporter of #16437 describes the same pressure: "The 7B DiT (6920 MB staged) plus the qwen3vl_8b text encoder (8916 MB staged) cannot co-reside in 16304 MB." The same report notes that a run with only the seed changed reuses the cached conditioning and reloads nothing — and it is a report of output corruption after exactly this reload, on AMD; see Troubleshooting.

The encoder stage is the binding one in both views: 8.959 GiB under DynamicVRAM (9.209 GiB on Windows), 9.899 GiB under the legacy loader, against 12 GiB. Neither loader budgets for the encoder's own activations, so an edit with several large reference images — which all pass through the encoder's vision tower — has an unestimated working set on top.

Why 12 GB is the floor for this page. 12 GB is the smallest card size in this catalogue on which every stage keeps its weights whole under both loaders; that is the definition this page's min_vram_gb uses. On 8 GB the template's int8 encoder cannot stay on the card — 8.709 GiB of weights plus 0.25 GiB of headroom on Linux (0.5 GiB on Windows) already exceeds it — so DynamicVRAM would evict and re-read it part-way through every encode, which nobody has measured. The RTX 5060 page in this series documents the install aimed at 8 GB (the w4a8 encoder).

At 2K the decode is what is tight. The 2048² decode estimate is 7.031 GiB, and the DiT's 6.758 GiB cannot stay beside it (14.669 GiB under DynamicVRAM, 14.809 GiB legacy). Under DynamicVRAM the room comes from evicting DiT weights, which then have to be re-read for the next image; under the legacy loader the DiT is moved off the card. ComfyUI also keeps a fallback for a decode that runs out anyway: it logs "Warning: Ran out of memory when regular VAE decoding, retrying with tiled VAE decoding." and tiles it. So 2K should complete; whether it completes in one pass on this card is precisely what nobody has measured.

Installation

1. ComfyUI v0.37.0 or newer, on a cu130 PyTorch

Support landed in ComfyUI core with PR #16400 (author kijai, merged 2026-09-19), and tag v0.37.0 contains it — the compare view shows the tag eight commits ahead of that merge and none behind. The v0.37.0 release itself was published at 07:35 UTC on 2026-09-21 — a day after the tag, so for the first day the only way in was git — and it carries Windows portable builds.

Windows portable. Take ComfyUI_windows_portable_nvidia.7z from that release. The ComfyUI README says "The portable above currently comes with python 3.13 and pytorch cuda 13.0." — which is the build this card needs. Do not take ComfyUI_windows_portable_nvidia_cu126.7z: the README labels it for GTX 10-series and older cards, adding "DO NOT USE THIS ON NEWER 20 SERIES AND ABOVE GPUS", and on this card a CUDA 12 build switches off the fast int8 kernels (step 2).

Git (Linux; on Windows use the portable, or swap the activation line as noted in the block). Install PyTorch from the cu130 index first, then the requirements — the torch line in requirements.txt is unpinned, so the order decides which build you get:

git clone https://github.com/Comfy-Org/ComfyUI
cd ComfyUI
python -m venv venv
source venv/bin/activate    # Windows cmd/PowerShell: venv\Scripts\activate
pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu130
pip install -r requirements.txt
python -c "import torch; print(torch.__version__, torch.version.cuda)"
git describe --tags

The print must show a +cu130 torch with 13.0 beside it (the cu130 index carries builds from torch 2.9.0 to 2.14.0), and git describe must read v0.37.0 or something later such as v0.37.0-5-g…. The README states the requirement as policy — "Using a cu130 or above version of pytorch is required on Nvidia 20 series and above." — and a CUDA 13 build needs an NVIDIA driver from the 580 branch or newer, the minimum NVIDIA's CUDA release notes give for CUDA 13.x. requirements.txt at the tag pins comfy-kitchen==0.2.35 and comfyui-workflow-templates==0.11.66; the latter is the templates release that switched the Qwen-Image-2.1 templates to the int8 encoder.

Tag or master? Two follow-up PRs merged after the tag and are on master, not in v0.37.0 or its portable: PR #16429, which changes where the edit path's K/V cache is placed — it matters on a card this full, see Editing below — and PR #16430, which adds ComfyUI's memory compiler to the 2.1 transformer blocks, aimed by its author at runs where offloading is the bottleneck. A git install tracking master has both; if you only generate from text, the tag is enough.

2. What Ampere gets from the int8 files, and what it does not

Read this before choosing an encoder, because the file names invite the wrong choice. Both int8 files carry, per layer, the quantisation format int8_tensorwise with convrot: true and a group size of 256 (read from each file's safetensors header). What happens to those weights differs between the two models.

For the diffusion model, ComfyUI decides per device which formats run natively in get_disabled_quant_formats() in comfy/ops.py, and prints the result when it loads it, as a line starting Native ops: followed by emulated ops:. For a compute-capability 8.6 device the checks in model_management.py resolve like this: supports_fp8_compute() is False (major 8, minor below 9), supports_nvfp4_compute() and supports_mxfp8_compute() are False (major below 10), and supports_int8_compute() is True — it has no compute-capability test at all and excludes only MPS, Intel XPU, DirectML and ixuca. So on this card the log lists int8_tensorwise, convrot_w4a4 and asym_w4a8_int8 as native, and nvfp4, mxfp8 and the two float8 formats as emulated. An emulated layer's weight is dequantised to bf16 for the matmul.

For the text encoder that table does not apply. At v0.37.0 every quantised encoder is built with mixed_precision_ops(..., full_precision_mm=True) in comfy/sd1_clip.py (the log says Using MixedPrecisionOps for text encoder), so each weight is dequantised for its matmul on every card. The one call that switches quantised matmuls back on, use_quantized_matmul, wraps only generate() — LLM text generation, not the conditioning encode this model uses. The encoder's file format is therefore a memory choice, not a speed choice. Three consequences:

  • The int8 DiT is the right file here, not a compromise. Its matmuls run as INT8 GEMMs. With a cu130 PyTorch, the comfy-kitchen CUDA backend rotates and quantises the activations in one fused kernel and runs the GEMM through its CUTLASS INT8 path, which its source (backends/cuda/__init__.py, pinned at the commit tagged v0.2.35) enables for compute-capability major 8 and above.
  • Without cu130 nothing breaks; it runs the slow way. The gate is in comfy/quant_ops.py: below CUDA 13 it calls ck.registry.disable("cuda") and logs "WARNING: You need pytorch with cu130 or higher to use optimized CUDA operations." comfy-kitchen then falls through its backend order (cuda, triton, eager; ComfyUI disables triton by default) to eager, whose int8_linear applies the Hadamard rotation as ordinary torch ops, quantises the activations per row, and still runs an integer GEMM through torch._int_mm before rescaling in chunks. That is a working INT8 path with the fusion gone — not a dequantisation to bf16, and not a failure. It is also why the Native ops line will not warn you: it reflects the hardware, not which comfy-kitchen backend is alive. The tells are the WARNING above, and the startup line Found comfy_kitchen backend cuda: showing 'disabled': True.
  • For the encoder, pick by size. Comfy-Org's encoder repo Comfy-Org/Qwen3-VL also holds qwen3vl_8b_fp8_scaled.safetensors (10,588,637,512 B = 9.861 GiB) and qwen3vl_8b_nvfp4.safetensors (6,305,221,764 B = 5.872 GiB). Both carry the DeepStack key and the 4096-wide merger that comfy/sd.py uses to identify Qwen3-VL-8B, so under type: qwen_image they would be routed to the 2.1 encoder class and should load; nobody in the three repos' discussion tabs reports running either with 2.1, and this page did not either. Since every encoder format is dequantised for the encode anyway, only size matters: the fp8 file is larger than the int8 one, and the nvfp4 file is within 7 MB of the w4a8 encoder that ships in the repack beside the int8 one. If the encoder stage is what you need to shrink, take w4a8 — it saves memory, not time.

Why the encoder identity is worth a paragraph: Comfy-Org's qwen3vl_8b_bf16.safetensors in the 2.1 repo carries the same LFS sha256 (68bdc82b…) as the file of that name in Comfy-Org/Qwen3-VL, uploaded there on 2026-06-06 to a repo whose card lists Qwen/Qwen3-VL-8B-Instruct as a base model — three months before the 2.1 weights were public. The vendor's own text_encoder/ shards sum to 17,534,339,488 B and the stock Qwen3-VL-8B-Instruct shards to 17,534,339,512 B, differently split. Two readers who compared the tensors reached the same answer on the vendor's discussion #5: "我比较过 tensor 数值 原版 instruct 没有增量训练" (FIT17, community: compared the tensor values — the original Instruct, no incremental training), and a second reply reports a tensor-by-tensor comparison with identical hashes. The encoder is the stock model, which is why encoder quants from the older repo are interchangeable in principle.

3. Download the weights

The repo's folder names match ComfyUI's models/ layout, so --local-dir models lands each file where the loaders look. On a git install, run this from inside the ComfyUI folder with the venv active — the shell step 1 left you in:

pip install -U huggingface_hub
hf download Comfy-Org/Qwen-Image-2.1 diffusion_models/qwen_image_2.1_int8_convrot.safetensors --local-dir models
hf download Comfy-Org/Qwen-Image-2.1 text_encoders/qwen3vl_8b_int8_convrot.safetensors --local-dir models
hf download Comfy-Org/Qwen-Image-2.1 vae/qwen_image_2.1_vae_bf16.safetensors --local-dir models

On the Windows portable there is no pip or hf on PATH. Use the Python the portable ships, python_embeded\python.exe — the same interpreter its run_nvidia_gpu.bat starts ComfyUI with and its updater runs pip through — from the portable's top folder, the one holding python_embeded and ComfyUI:

python_embeded\python.exe -s -m pip install -U huggingface_hub
python_embeded\python.exe -s -m huggingface_hub.cli.hf download Comfy-Org/Qwen-Image-2.1 diffusion_models/qwen_image_2.1_int8_convrot.safetensors --local-dir ComfyUI\models
python_embeded\python.exe -s -m huggingface_hub.cli.hf download Comfy-Org/Qwen-Image-2.1 text_encoders/qwen3vl_8b_int8_convrot.safetensors --local-dir ComfyUI\models
python_embeded\python.exe -s -m huggingface_hub.cli.hf download Comfy-Org/Qwen-Image-2.1 vae/qwen_image_2.1_vae_bf16.safetensors --local-dir ComfyUI\models

(-m huggingface_hub.cli.hf is the module behind the hf command; it runs directly because cli/hf.py ends in a __main__ guard.)

Check the sizes against the table above. If you settle on the smaller encoder, the fourth file is text_encoders/qwen3vl_8b_w4a8.safetensors from the same repo — select it in the template's CLIP loader. Do not confuse it with the two qwen3.5_9b_qwen_image_2.1_pe_*.int8_convrot.safetensors files beside it: those are Qwen3.5-9B prompt-rewriting models for an optional LLM stage, and a reader on the repack's discussion #4 who swapped one in as the text encoder got garbled images. As another reader put it in the same thread, "The PE models only for PE." (Landsharkbaby, community). At 9,471,072,252 B each they are another encoder-sized load on a card that already cannot hold one beside the DiT; skip them here.

Running

Start ComfyUI normally — no --lowvram, no --reserve-vram, nothing:

python main.py

On this card with a cu130 PyTorch, DynamicVRAM is on by default at v0.37.0: main.py enables it on NVIDIA when PyTorch is 2.8 or newer and comfy-aimdo initialises, and the log then reads DynamicVRAM support detected and enabled. Under it the text encoder runs on the GPU, weights that do not fit are paged rather than refused, and --lowvram is documented in comfy/cli_args.py as "Doesn't do anything if dynamic vram is enabled." — so adding it out of 12 GB habit changes nothing. You do not need a flag for the desktop: comfy-aimdo's live check keeps 0.25 GiB free on Linux (0.5 GiB on Windows) after whatever the desktop already holds (the desktop still comes out of the card's 12 GiB, see Requirements). If you want more kept free anyway, use --vram-headroom N, which adds N GiB on top of both of comfy-aimdo's checks. --reserve-vram N is weaker under DynamicVRAM — it only raises the check against the card's total capacity — and it is also what comfy/model_management.py uses as the legacy loader's reserve.

Text-to-image

Open the Template Library and load Qwen Image 2.1: Text to Image (image_qwen_image_2_1_t2i.json, read here at workflow-templates v0.11.66, the version v0.37.0 pins). The values that govern it sit on the outer subgraph node: UNETLoaderqwen_image_2.1_int8_convrot.safetensors, CLIPLoaderqwen3vl_8b_int8_convrot.safetensors with type qwen_image, VAELoaderqwen_image_2.1_vae_bf16.safetensors, 25 steps, cfg 1, euler / simple, 1024×1024. The template's own note explains the setting people change first: "negative_prompt: unused while cfg is 1." At cfg 1 ComfyUI skips the negative pass entirely; raising cfg adds a second model pass on every step. For native 2K the note says to set the Resolution Selector to 1:1 at 4 megapixels. The vendor's diffusers example runs 40 steps; the template's 25 is ComfyUI's choice, not Qwen's.

For a transparent image, wrap the prompt the way the model card does — This is an RGBA image with transparency. <your subject>. The image has alpha channel and the background is transparent. — and keep the template's SaveImageAdvanced at PNG, 8-bit, sRGB, which keeps the alpha channel.

Expect the first run to encode the prompt, sample, then decode; expect a prompt change to repeat the encode and move weights back and forth; expect a seed-only change to skip the encode.

Editing with reference images

Load Qwen Image 2.1: Image Edit (image_qwen_image_2_1_image_edit.json, same release). Same three loaders, plus a TextEncodeQwenImage21 node with the VAE connected and ten reference inputs exposed (the node has sixteen; the model card says up to ten), and a QwenImage21Cache node at auto / default. Reference images are seen by the encoder's vision tower and, because the VAE is connected, spliced into the sequence as latents. The text and reference tokens are the same on every step, so their attention K/V are computed once and cached — PR #16400 puts the gain at about 1.7× on edits. The cache node's tooltip in comfy_extras/nodes_qwen.py on where that cache goes: "auto uses spare VRAM, then RAM. cpu (RAM) is prefetched behind compute and costs little speed."

On a 12 GB card, where the cache lands decides the speed. The cache costs 2 × 32 layers × 4096 × 2 bytes = 0.5 MiB per cached token, so one 1024×1024 reference (4,096 latent tokens) is 2 GiB before the text. At v0.37.0 comfy/ldm/qwen_image21/model.py places it in auto mode only if four times that size is free — on the GPU first, then in system RAM — and otherwise recomputes the whole prefix on every step. With the DiT already filling the card, that means about 8 GiB of free RAM per 1024² reference, and on a 16–32 GB machine that may not be there. PR #16429, on master only, exists because this check was too strict; its author lists two problems: "It requires the VRAM to be free at the time of the check to use VRAM mode which gets disrupted by smart memory + dyn vram" and "It requires 4x RAM to be free and doesnt account the pin quotas at all". On the PR's own test machine — an RTX 5080 with 32 GB RAM, a newer and faster card than this one — an edit with two references went from the full-recompute fallback at 4.01 s/it to 1.02 it/s. If you run the tag and edits are slow, set the cache node's device to cpu: that skips the four-times check (the source still wants twice the cache size free in RAM), or move to master.

The template sets the encode node's resolution to 0, and its note says why: "This template starts at 0: no resize beyond a multiple of 32." That default interacts with an open defect in which certain reference sizes render the edit as noise, and the two reports so far disagree on which sizes — read the Troubleshooting entry before you rely on resolution=0.

Smaller DiT builds: what they change on 12 GB

The binding stage on this card is the text encoder, so a smaller transformer does not move the ceiling; the encoder is the lever (w4a8, above). What a smaller DiT would change is 2K: a transformer that stays resident beside the 2048² decode is not evicted and re-read for every image. The room left beside that decode is 4.090 GiB under DynamicVRAM (12 − 7.910) and 3.949 GiB under the legacy loader (12 − 8.051), before the desktop's share and the CUDA context. The Q4 GGUFs and the mixed NidAll/Qwen-Image-2.1-Mixed-Balanced build (4,203,397,480 B = 3.915 GiB) squeeze under the line but leave only 0.034–0.309 GiB for the CUDA context and any desktop together — too little to count on, so in practice they would be evicted too. The Q3-and-smaller GGUFs and the INT4 ConvRot build leave a usable margin — for example, left under DynamicVRAM: Abiray's Q3_K_M 1.122 GiB, leejet's Q3_K (3,270,553,632 B = 3.046 GiB) 1.044 GiB, leejet's Q2_K 1.704 GiB, INT4 ConvRot 0.675 GiB. That 0.675 GiB has to hold the CUDA context and the desktop together, which is thin on a Windows desktop. Q5_K_M (4.664 GiB) does not fit at all. These are Linux figures; on Windows DynamicVRAM keeps 0.25 GiB more free, which leaves INT4 ConvRot 0.425 GiB and puts the mixed build over the line. None of the builds below has a published run on a named 12 GB or Ampere card, and all appeared within a day of release.

  • GGUF. Abiray/Qwen-Image-2.1-GGUF publishes Q8_0 at 7,591,579,808 B = 7.070 GiB, down through Q4_K_M at 4,189,343,904 B = 3.902 GiB (11.812 GiB beside a 2K decode under DynamicVRAM, 11.953 GiB legacy) to Q3_K_M at 3,185,944,736 B = 2.967 GiB; leejet's smallest, Q2_K, is 2,561,716,256 B = 2.386 GiB. Its headers (read over an HTTP range request) declare general.architecture = qwen_image, which city96's loader.py accepts, and ComfyUI v0.37.0 then recognises the 2.1 key layout — so the stock loader should take them, as the card says. leejet/Qwen-Image-2.1-GGUF is different: its files carry no metadata keys at all (a stable-diffusion.cpp artifact), which sends the loader to its key-detection table, and only leejet's fork has a 2.1 entry in it. The two cards disagree about which loader to use, and each is right about its own files. ComfyUI-GGUF dequantises each weight to the compute dtype before the matmul, so Q8_0 saves nothing against the int8 file and gives up the INT8 GEMM.
  • INT4 ConvRot (W4A4). toxicdog/Qwen-Image-2.1-INT4ConvRot-ComfyUI ships a 3,666,512,184 B = 3.415 GiB DiT whose header declares the convrot_w4a4 format — native on this card by the table above — and comfy-kitchen gates its fused 4-bit kernel on compute-capability major 8, Ampere and Ada. Beside a 2K decode it comes to 11.325 GiB under DynamicVRAM (11.466 GiB legacy), and unlike the GGUFs it keeps a native matmul. Quality at 4-bit activations is the open question.
  • Not for this card: NVFP4. The NVFP4 DiT builds load, but supports_nvfp4_compute() wants major ≥ 10, so on Ampere they are emulated: the memory saving survives and the kernel does not.

Results

  • Speed: omitted. No timing on an RTX 3060, on any 12 GB card or on any Ampere card has been published on the surfaces this page searched (listed in the note at the top). The figures that exist name an RTX 4090 (a Hacker News comment on the launch thread), an RTX 5080 (PR #16429) and an RTX 5090 (PR #16430) — all newer and faster, so none of them says anything useful about this card. If you time a run — steps, resolution, seconds per image, whether the decode fell back to tiled — post it; it becomes the first datapoint on /check/qwen-image-2-1/rtx-3060.
  • VRAM usage: derived, not measured. The binding stage is the text encoder. Under DynamicVRAM, the default on this card: 8.709 GiB of weights plus comfy-aimdo's headroom — on Linux 0.25 GiB, 8.959 GiB against 12 GiB, 3.041 GiB spare; on Windows 0.5 GiB, 9.209 GiB and 2.791 GiB spare — before the desktop's share and the CUDA context, which have to fit inside it (the live check counts them when it decides what to evict, so no flag is needed for them). Under the legacy loader (--disable-dynamic-vram and friends): 8.709 GiB plus ComfyUI's 1.191 GiB reserve — 9.899 GiB, 2.101 GiB spare before the same desktop and context (1.905 GiB on Windows). The denoise stage is 7.488 / 8.928 GiB at 1024² / 2048² under DynamicVRAM (7.949 / 9.069 GiB legacy), and the 2K decode is 7.910 GiB (8.051 GiB legacy) with the DiT off the card. No measured peak at native 2K on any consumer card was found — the one 2048² run on record, an RTX 5090 batch in PR #16430, reports step time and the staged DiT size, not a VRAM peak.
  • Quality notes: the VAE has a reported grid artifact. The vendor's discussion #12, titled "Diamond grid pattern caused by VAE", says "It's more noticeable when you generate a high-resolution image that has people with light skin tones and zoom in on their faces." (Rkss, community), and that a plain encode–decode round trip shows it. A reply lists mitigations — a GLSL post-process node, a node pack, or re-encoding through the Flux VAE — none of them a fix, and none tied to a resolution or a card. Asked whether the VAE or the transformer is responsible, the thread's author answered that it is the VAE and pointed back to the encode–decode test; that is a community answer, not the vendor's. Inspect native-2K portraits at 100%.

For the full benchmark data, see /check/qwen-image-2-1/rtx-3060.

Troubleshooting

TextEncodeQwenImage21 or QwenImage21Cache missing when the template loads

You are below v0.37.0. Update to the v0.37.0 portable or git pull, then check the version line at startup. The ComfyUI docs page lists not running the latest version as the first reason nodes go missing, and nodes that failed to import at startup as the second.

The startup log says you need cu130

That is the quant_ops.py gate above: your PyTorch was built against CUDA 12.x — on Windows, usually the cu126 portable. Everything still runs — the diffusion model on the eager INT8 path; reinstall PyTorch from the cu130 index (or switch to the nvidia portable) and confirm torch.version.cuda prints 13.0. A different line, Failed to import comfy_kitchen, is worse: the int8 formats these files use are defined by that package, so fix the import — pip install -r requirements.txt in the ComfyUI venv restores the pinned 0.2.35 — before you debug anything else.

Edit output is over-sharpened and speckled, or ignores the edit; text-to-image is clean

Open defect #16435, with two reports that disagree on which reference sizes trigger it. With the VAE connected to TextEncodeQwenImage21, the original reporter (Apple MPS and CPU, bf16 DiT) got broadband noise whenever the reference resolved to a 64×64 latent grid — a 1024×1024 reference at resolution=0, or resolution=1024 — while 992 and 1056 were clean. A second reporter on CUDA — an RTX 5070 Ti, v0.37.0, torch 2.13.0+cu130, the official edit graph with a bf16 DiT — found that 64×64 grid clean, but a 1536×1024 reference (grid 96×64) broken: over-sharpened, speckled, and with the edit instruction ignored, both at resolution=0 and at resolution=1248, which resolve it to the same size. On that machine resolution 512, 1024, 1056 and 2048 were clean. So the trigger is the resolved grid, not the parameter value. The two reports disagree on which grids fail. The backend is one possible explanation, but the two setups also differ in encoder precision, reference image, step count, OS, ComfyUI build and attention path, so neither report isolates a cause. The second reporter notes that both failing grids are multiples of 2048, which is an observation, not a confirmed cause. Nobody has reported on an RTX 3060 or any Ampere card. resolution 1056 and 512 were clean in both reports — use 1056 instead of the template's 0 if you see the artifact; 512 avoids it too, but shrinks the reference. Disconnecting the VAE from the node also removes it, at the cost of reference adherence (on CUDA the requested edit was no longer performed at all). A proposed fix, PR #16444, is currently marked draft by its author; it was validated with unit checks only, and its description notes that full model reproduction was unavailable. (Its sibling report, #16433 — VAE encode broken on Apple's MPS backend — does not apply to CUDA.)

Edits are far slower than text-to-image

Most likely the K/V cache found no room and fell back to recomputing the prefix every step — see Editing above. Set QwenImage21Cachedevice to cpu, try dtype int8 (the node's tooltip says it halves the cache at about bf16 accuracy), shrink the references, or move to master for PR #16429.

Shape error [1, 338, 5120] from the text encoder

The CLIP loader was given something other than the 4096-wide Qwen3-VL-8B. The reporter of the repack's discussion #2 saw exactly this error while trying the w4a8 encoder, then closed the thread as having loaded the wrong encoder. Load qwen3vl_8b_int8_convrot.safetensors or qwen3vl_8b_w4a8.safetensors — not a qwen3.5_9b_…_pe_… file.

2K runs but the log says it retried the VAE decode tiled

That is the fallback working as designed; the 7.031 GiB decode estimate at 2048² is the largest working-set estimate on this page. The decode is slower, and the image should be unchanged. A smaller DiT does not change this under either loader: DynamicVRAM already evicts DiT weights when the decode needs the room, and the legacy loader already moves the DiT off the card before the decode. A DiT small enough to stay beside the decode only saves re-reading it for the next image. What removes the tiling is more free VRAM — close what else is using the card.

Every generation reloads models

Expected when the prompt or the resolution changes, for the co-residence reason in Requirements; a seed-only change should not. If a seed-only change also reloads, something upstream of the sampler changes on every run.

Those reloads are the trigger of an open defect. Issue #16437, titled "Qwen-Image 2.1: --enable-dynamic-vram silently corrupts output after any model reload (channel slice offset, ROCm gfx1201)", reports that on an RX 9070 XT the first image after a server start is correct, and every image after the first reload — a prompt change is enough — comes back with its channels in a [B, A, B, A] pattern, subjects rendering bright green, until the server is restarted. Removing --enable-dynamic-vram fixed it for the reporter, who adds "I don't have an NVIDIA card to test whether this is ROCm-specific." No report of it on NVIDIA was found (the ComfyUI tracker's issues of the launch window were read), and the one other commenter in the thread was running a different model. If your images turn green or wrong-coloured after a prompt change, restart ComfyUI; if it keeps happening, start it with --disable-dynamic-vram, which puts you on the legacy loader and its budget in Requirements.

See also: the 2025 20B predecessor has its own page for this card, on a GGUF Q3 path that the much smaller 2.1 no longer needs — Qwen-Image on RTX 3060. Report problems and post measurements via the submission form.

common questions
How much VRAM does Qwen-Image-2.1 need?

About 12 GB — the minimum this recipe targets.

Which GPUs is Qwen-Image-2.1 tested on?

RTX 3060 (12 GB).

How hard is this setup?

Intermediate — follow the steps above.

next