AI

Run Wan Video Generation Locally on a 24GB GPU

The Wan 2.2 README names the RTX 4090 by model number as a card its 5B video generator runs on. On a 4090 the documented command gets through all 50 denoising steps, spends nine minutes doing it, then dies at the final decode with 2.19 GiB free. Local Wan video generation on a 24 GB card is real, but the path the documentation hands you is not the one that finishes.

Original content from computingforgeeks.com - post 171832

Benchmarked September 2026 on Wan 2.2 (single RTX 4090, 24 GB).

This covers the whole path on one consumer GPU: which of the five Wan models fit in 24 GB and which need 80, the four packages the project forgets to declare, the allocator flag that turns the out-of-memory crash into a finished clip, and the quantized ComfyUI route that puts the 14 billion parameter model on a 12 GB card. The sizing logic assumes some familiarity with VRAM budgeting for local models, and the ComfyUI half builds on the same install covered in the Stable Diffusion on Linux guide.

Here is the whole thing in 67 seconds: the documented command crashing, the one line that fixes it, then the two clips this card produced.

Terminal replay of the two TI2V-5B runs, then the clips from the fixed run and the ComfyUI GGUF run

How the video was put together: the terminal part replays the saved session logs of the two runs line for line, sped up because each run took about ten minutes. The progress bar shows four of its 50 updates, and “…” marks where traceback frames were cut. The last 20 seconds are the real output: the MP4 from the fixed TI2V-5B run, then the animated WebP from the ComfyUI run (covered in Step 5) converted to video. Each clip plays twice. The only processing was re-encoding for the web, plus scaling the 480p clip to fill its frame. No frame interpolation, no retouching.

Where the Wan line actually stands

Searches still land on Wan 2.1, so start with what the GitHub organisation holds today. Five repositories, all Apache-2.0:

RepositoryStarsLast push
Wan2.217,6062026-09-21
Wan-Animate-23242026-08-08
Wan-Dancer4342026-07-17
Wan-skills762026-04-17
Wan2.117,0302026-03-05

Wan 2.1 has not been touched in roughly six months. Wan 2.2 was pushed two days before this test. There is no 2.5 or 2.6 repository at all. Those versions exist, but as a hosted API on Alibaba Cloud with no weights published. Open weights stop at 2.2, so any install guide still pointing at the 2.1 tree is sending readers to a frozen branch.

The 2.2 release moved text-to-video and image-to-video to a mixture-of-experts design. Two 14B expert models split the denoising schedule, one handling high-noise timesteps and one handling low. Only one expert is resident at a time, which matters more than it sounds like it should once quantization enters the picture.

Almost nobody downloads the official weights

Hugging Face publishes a 30-day download count per repository. Pulling those for the official Wan-AI releases and for the community repackages tells a blunt story:

RepositoryDownloads / 30dSize
Comfy-Org/Wan_2.2_ComfyUI_Repackaged5,725,358728.7 GB
Comfy-Org/Wan_2.1_ComfyUI_repackaged2,915,359487.4 GB
QuantStack/Wan2.2-T2V-A14B-GGUF731,285250.0 GB
QuantStack/Wan2.2-I2V-A14B-GGUF280,921232.2 GB
unsloth/Wan2.2-TI2V-5B-GGUF115,22345.4 GB
Wan-AI/Wan2.2-Animate-14B (official)57,74572.4 GB
Wan-AI/Wan2.2-TI2V-5B (official)20,87634.2 GB
Wan-AI/Wan2.2-I2V-A14B (official)11,883126.2 GB
Wan-AI/Wan2.2-T2V-A14B (official)3,146126.2 GB

Every official Wan 2.2 checkpoint put together drew 104,727 downloads in 30 days. The repackaged and quantized copies drew 10,069,085, a ratio of 96 to 1. The flagship text-to-video model is the starkest case: 3,146 people pulled the 126 GB official release while 731,285 pulled a quantized build of the same weights.

That gap is a hardware verdict. The official release is bf16 and assumes a data-center card. The quantized copies are what fits on the GPUs people own. Both routes appear below, because the official one is still the reference implementation and it is where the model’s real memory behaviour shows up honestly.

Which Wan model fits which card

Wan 2.2 ships five models. The README states a VRAM floor per task, and only one of them is a consumer number:

ModelJobDownloadStated VRAM floor
T2V-A14BText to video126.2 GB80 GB
I2V-A14BImage to video126.2 GB80 GB
S2V-14BAudio driven video49.1 GB80 GB
Animate-14BCharacter animation and replacement72.4 GB80 GB class
TI2V-5BText and image to video at 720p34.2 GB24 GB

TI2V-5B carries the entire consumer story in the official repo. It uses a higher-compression VAE at 16x16x4, which is what buys the memory saving. The README’s wording is specific: this task “can run on a GPU with at least 24GB VRAM (e.g, RTX 4090 GPU)”.

One structural detail decides how much slack that leaves. The supported resolutions per task live in wan/configs/__init__.py:

SUPPORTED_SIZES = {
    't2v-A14B': ('720*1280', '1280*720', '480*832', '832*480'),
    'i2v-A14B': ('720*1280', '1280*720', '480*832', '832*480'),
    'ti2v-5B': ('704*1280', '1280*704'),
    's2v-14B': ('720*1280', '1280*720', '480*832', '832*480', '1024*704',
                '704*1024', '704*1280', '1280*704'),
    'animate-14B': ('720*1280', '1280*720')
}

The 14B models accept 480p. The 5B model, the only one sold as consumer-friendly, accepts 720p and nothing else. There is no resolution to drop to when memory runs short. Step 4 is where that bites.

Step 1: Confirm the card and the driver

PyTorch needs the NVIDIA driver, not the full CUDA toolkit. Check what the box reports before pulling 34 GB of weights:

nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv

The test box reports a single 24 GB card:

name, memory.total [MiB], driver_version
NVIDIA GeForce RTX 4090, 24564 MiB, 580.119.02

Note the real figure. The card holds 24,564 MiB and CUDA reports 23.52 GiB usable once the driver takes its share. Wan’s 24 GB claim is measured against the marketing number, not the one the allocator gets to spend.

Torch has to be recent enough for the project and built with bfloat16 support:

python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_bf16_supported())"

Anything from 2.4 onward satisfies the requirements file:

2.5.1+cu121 12.1 True
Wan 2.2 test box showing RTX 4090 with 24564 MiB and driver 580.119.02

Step 2: Clone Wan and fix the dependency list

The clone is small. The weights come separately in step 3.

git clone https://github.com/Wan-Video/Wan2.2.git
cd Wan2.2

Now the part that costs people an afternoon. The shipped requirements.txt lists flash_attn, which compiles CUDA kernels from source and routinely takes half an hour or fails outright on a mismatched toolchain. Install everything else first:

grep -v '^flash_attn' requirements.txt > /tmp/req-noflash.txt
pip install -r /tmp/req-noflash.txt

That completed in 83 seconds on the test box. It is also not enough, because the requirements file is incomplete. Importing the package fails four separate times, one missing module per attempt:

python -c "import wan"

The first failure comes from the VAE module:

  File "/root/Wan2.2/wan/modules/vae2_1.py", line 8, in <module>
    from einops import rearrange
ModuleNotFoundError: No module named 'einops'
Four Python modules missing from the Wan 2.2 requirements file

Fixing that one exposes the next, and so on. Four packages are imported by the code and absent from the requirements file:

pip install einops decord librosa peft

Now flash_attn. Skipping it looks safe, because the import is wrapped in a try block:

try:
    import flash_attn
    FLASH_ATTN_2_AVAILABLE = True
except ModuleNotFoundError:
    FLASH_ATTN_2_AVAILABLE = False

It is not safe. Further down the same file, flash_attention() opens with assert FLASH_ATTN_2_AVAILABLE, and wan/modules/model.py calls it directly rather than going through the wrapper that has a fallback. Without the package the model loads, the weights read, and inference dies on a bare AssertionError with no message attached. The guarded import buys nothing.

Rather than building it, take a prebuilt wheel matching the exact Python, Torch and C++ ABI combination on the box. Confirm the ABI flag first, because picking the wrong one produces a wheel that installs and then fails to import:

python -c "import torch, sys; print(torch._C._GLIBCXX_USE_CXX11_ABI, sys.version_info[:2], torch.__version__)"

False, Python 3.10 and Torch 2.5 on this box, which maps to one filename on the release page:

export FA_VER=v2.8.3.post1 #https://github.com/Dao-AILab/flash-attention/releases
pip install "https://github.com/Dao-AILab/flash-attention/releases/download/${FA_VER}/flash_attn-2.8.3.post1%2Bcu12torch2.5cxx11abiFALSE-cp310-cp310-linux_x86_64.whl"

Ten seconds against a build that can run past thirty minutes. Verify it imports:

python -c "import flash_attn; print(flash_attn.__version__)"

Step 3: Pull the TI2V-5B weights

The Hugging Face CLI does the download. Installing it has a trap worth taking in the right order:

pip install -U "huggingface_hub[cli]" hf_transfer
export HF_HUB_ENABLE_HF_TRANSFER=1
hf download Wan-AI/Wan2.2-TI2V-5B --local-dir ./Wan2.2-TI2V-5B

That -U pulls hub 1.x, which Wan’s pinned transformers refuses to work with. The breakage only surfaces later, at generation time:

ImportError: huggingface-hub>=0.30.0,<1.0 is required for a normal functioning of
this module, but found huggingface-hub==1.32.0.

Downgrade after the download finishes, not before, since the CLI needs the new version to run and the library needs the old one:

pip install "huggingface-hub>=0.30.0,<1.0"

Check what landed. The download is 34.2 GB across 23 files, and 11.36 GB of that is the umT5-XXL text encoder that every Wan model shares:

du -sh ./Wan2.2-TI2V-5B

Budget disk accordingly. A box that only has 50 GB free will finish this download and then have nowhere to put a second model.

Step 4: Run Wan video generation and meet the memory wall

This is the command the README gives for a consumer card, unmodified. The three flags after the checkpoint path are the memory-saving ones: weights get offloaded between stages, parameters are cast down, and the text encoder stays on the CPU.

python generate.py --task ti2v-5B --size 1280*704 \
  --ckpt_dir ./Wan2.2-TI2V-5B \
  --offload_model True --convert_model_dtype --t5_cpu \
  --prompt "A lone hiker in a red jacket crosses a windswept mountain ridge at sunrise, golden light, drifting mist, slow cinematic camera push"

The defaults it resolves matter for reading the numbers that follow. Frame count is 121 at 24 fps, so just over five seconds of video, across 50 denoising steps with the unipc solver:

INFO: Generation job args: Namespace(task='ti2v-5B', size='1280*704', frame_num=121,
  offload_model=True, t5_cpu=True, sample_solver='unipc', sample_steps=50,
  sample_shift=5.0, sample_guide_scale=5.0, convert_model_dtype=True, ...)

Loading takes about 50 seconds: the umT5 encoder to system RAM, then the VAE, then the transformer to the GPU. Denoising then runs at a steady 9.92 seconds per step, holding about 21,750 MiB resident with the GPU pinned at 100 percent. Fifty steps take 8 minutes 13 seconds of pure diffusion.

All 50 steps complete. Then the VAE decode allocates, and on a 4090 it does not fit:

  File "/root/Wan2.2/wan/modules/vae2_2.py", line 40, in forward
    x = F.pad(x, padding)
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.60 GiB. GPU 0 has a
total capacity of 23.52 GiB of which 2.19 GiB is free. Including non-PyTorch memory,
this process has 21.32 GiB memory in use. Of the allocated memory 18.43 GiB is
allocated by PyTorch, and 2.40 GiB is reserved by PyTorch but unallocated.
Wan TI2V-5B out of memory at the VAE decode on an RTX 4090

Nine minutes and eighteen seconds of work, peak usage 24,037 MiB of the 24,564 MiB on the card, and no file written. The documented command, at the only resolution the model supports, on the exact card the documentation names.

Read the last sentence of that error again. 2.40 GiB was reserved by the allocator but unallocated, against a 2.60 GiB request that failed. The memory exists; it is fragmented into pieces too small to serve one large contiguous tensor. Video decode allocates in big blocks, and PyTorch’s default caching allocator handles that pattern badly after 50 steps of churn.

The fix is an environment variable, set before the run:

export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True

That switches the caching allocator to expandable segments, which grow in place instead of carving VRAM into fixed blocks that never quite fit the next request. Same command, same resolution, same flags, second run:

100%|##########| 50/50 [08:12<00:00,  9.85s/it]
INFO: Saving generated video to ti2v-5B_1280*704_1_A_lone_hiker_in_a_red_jacket_...mp4
INFO: Finished.

Exit code 0 and a 12.2 MB file on disk. Probing it confirms the model did what it advertised: H.264, 1280×704, 121 frames at 24 fps, 5.04 seconds.

The 5 second, 720p clip Wan TI2V-5B generated on the RTX 4090 in 667 seconds

That clip came entirely from the prompt in the command above, with no input image and no prompt extension. The run log shows it drew the random seed 304889411615507904. To start from the same noise, add --base_seed 304889411615507904 to the command. Expect a close match, not an identical clip, on a different GPU or PyTorch build. The 5B model is at its best on the grass, the mist and the light. It is weakest on the person: as the hiker turns to walk out of frame, the backpack grows from two shoulder straps into a full pack, and the legs smear at the fastest part of each stride.

The comparison between the two runs is the useful part:

RunAllocatorPeak VRAMWall timeResult
1default24,037 MiB558 sOOM at VAE decode
2expandable_segments24,079 MiB667 svideo written

The successful run peaked 42 MiB higher than the one that crashed. Capacity was never the problem. The allocator’s layout was. Anyone sizing a card off the crash message alone would reach for a bigger GPU and fix nothing.

What the numbers do say is that 24 GB is the floor, not a comfortable target. Peak usage landed at 24,079 MiB against 24,564 MiB installed, leaving under 500 MiB of headroom. A second CUDA process, a desktop session, or a browser with hardware acceleration on will push this back into the crash. Run it on a headless box.

Step 5: Run the 14B model in ComfyUI on quantized weights

The 5B model is the one the official repo supports on a consumer card. It is not the one most people run. That 96-to-1 download ratio points at ComfyUI with GGUF checkpoints, and the arithmetic explains why the flagship becomes reachable there.

T2V-A14B is a mixture of experts. Two separate 14B models cover different halves of the denoising schedule, and only one sits in VRAM at a time. Quantize each expert and the resident footprint is one file, not the pair:

QuantizationPer expertBoth on diskCard it targets
Q2_K5.30 GB10.6 GB8 GB
Q3_K_M7.17 GB14.3 GB10 GB
Q4_K_M9.65 GB19.3 GB12 GB
Q6_K12.00 GB24.0 GB16 GB
Q8_015.40 GB30.8 GB24 GB
bf16 (official)~28.6 GB126.2 GB total repo80 GB

Q4_K_M is the usual landing spot. Install ComfyUI and the GGUF loader node into their own virtual environment, kept separate from the Wan repo so the two dependency sets cannot fight:

git clone https://github.com/comfyanonymous/ComfyUI.git
cd ComfyUI
python3 -m venv ~/cvenv
~/cvenv/bin/pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
~/cvenv/bin/pip install -r requirements.txt
git clone https://github.com/city96/ComfyUI-GGUF.git custom_nodes/ComfyUI-GGUF
~/cvenv/bin/pip install -r custom_nodes/ComfyUI-GGUF/requirements.txt

Four files go in three directories. Both experts, the shared text encoder at fp8, and the 2.1-series VAE that the 14B models use:

export HF_HUB_ENABLE_HF_TRANSFER=1
hf download QuantStack/Wan2.2-T2V-A14B-GGUF \
  HighNoise/Wan2.2-T2V-A14B-HighNoise-Q4_K_M.gguf --local-dir /tmp/q
hf download QuantStack/Wan2.2-T2V-A14B-GGUF \
  LowNoise/Wan2.2-T2V-A14B-LowNoise-Q4_K_M.gguf --local-dir /tmp/q
mv /tmp/q/HighNoise/*.gguf /tmp/q/LowNoise/*.gguf models/unet/

hf download Comfy-Org/Wan_2.1_ComfyUI_repackaged \
  split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors --local-dir /tmp/te
mv /tmp/te/split_files/text_encoders/*.safetensors models/text_encoders/

hf download Comfy-Org/Wan_2.1_ComfyUI_repackaged \
  split_files/vae/wan_2.1_vae.safetensors --local-dir /tmp/v
mv /tmp/v/split_files/vae/*.safetensors models/vae/

That is 26.3 GB on disk against 126.2 GB for the official release of the same model. Start the server:

~/cvenv/bin/python main.py --listen 127.0.0.1 --port 8188

The graph is two samplers in series rather than one. A KSamplerAdvanced runs the high-noise expert over the first half of the schedule and hands its latent, noise still in it, to a second sampler carrying the low-noise expert. Getting that handoff wrong is the most common reason a Wan 2.2 workflow produces mush: the first sampler needs return_with_leftover_noise enabled and the second needs add_noise disabled, with start_at_step on the second matching end_at_step on the first.

Running that graph at 832×480 for 81 frames over 20 steps, split 10 and 10 between the experts, the server log answers the question the whole article is circling:

INFO: Total VRAM 24081 MB, total RAM 106227 MB
INFO: Requested to load WAN21
INFO: loaded completely; 16335.16 MB usable, 9335.62 MB loaded, full load: True
ComfyUI loading a quantized Wan T2V-A14B expert in 9.3 GB of VRAM

An expert of the model the README puts at 80 GB loaded in 9,335 MB. Across the full run, including both experts, the text encoder and the decode, the card peaked at 14,907 MiB and finished in 8 minutes 36 seconds.

Set the two runs side by side, with the caveat that they are deliberately not matched. Each uses the settings its own path treats as normal:

Official CLIComfyUI + GGUF
ModelTI2V-5B, bf16T2V-A14B, Q4_K_M
Stated VRAM floor24 GB80 GB
Resolution1280×704832×480
Frames12181
Steps5020
Weights on disk34.2 GB26.3 GB
Peak VRAM24,079 MiB14,907 MiB
Wall time667 s516 s

Because the resolutions and step counts differ, the wall times are not a fair speed comparison and should not be read as one. The VRAM column is the finding. The bigger model, quantized, left 9 GB of headroom on a card where the smaller official model left under 500 MiB. That is the whole reason the community downloads run 96 to 1 in favour of the quantized builds.

The same prompt through quantized T2V-A14B in ComfyUI: 832×480, 81 frames, peak 14,907 MiB

Here are the exact node settings behind that clip. With the same graph and seed you start from the same noise:

NodeSetting
UnetLoaderGGUF (x2)HighNoise and LowNoise Q4_K_M experts
ModelSamplingSD3 (x2)shift 8.0 on each expert
CLIPLoaderumt5_xxl_fp8_e4m3fn_scaled, type wan
Positive promptA lone hiker in a red jacket crosses a windswept mountain ridge at sunrise, golden light, drifting mist, slow cinematic camera push
Negative promptblurry, low quality, jpeg artifacts, static, watermark, text
EmptyHunyuanLatentVideo832 x 480, length 81, batch 1
KSamplerAdvanced (high noise)seed 12345, 20 steps, cfg 3.5, euler, simple, steps 0 to 10, leftover noise on
KSamplerAdvanced (low noise)add_noise off, same seed and sampler, steps 10 to end, leftover noise off
VAELoaderwan_2.1_vae
SaveAnimatedWEBP16 fps, lossless off, quality 85

ComfyUI writes an animated WebP. The player above shows the same 81 frames repackaged as MP4 at 16 fps. That gives it player controls, and it comes out at 333 KB against the WebP’s 893 KB. It is the same positive prompt as the 5B run, and the 14B model read it differently: a small figure against a wide sunrise instead of a close walking shot. Compare it with the 5B clip above. The 14B clip handles the wide scene, the depth and the backlight better. The 5B clip wins on resolution and close-up detail.

Errors this install actually throws

Every one of these came out of the lab box while following the project’s own instructions.

ModuleNotFoundError: No module named ‘einops’

Raised by wan/modules/vae2_1.py the first time you import the package. The requirements file never declares it. Three more follow it in sequence, so install all four at once: einops, decord, librosa, peft.

AssertionError with no message, part way through generation

Traces back to assert FLASH_ATTN_2_AVAILABLE in wan/modules/attention.py. It means flash-attn is not installed. The guarded import at the top of that file suggests the dependency is optional; it is not, because the model calls flash_attention() directly. Install the prebuilt wheel matching your Python, Torch and ABI rather than building from source.

ImportError: huggingface-hub>=0.30.0,<1.0 is required for a normal functioning of this module

Self-inflicted by installing the Hugging Face CLI with -U after the requirements file pinned transformers. Pin the library back down once the weights are on disk: pip install "huggingface-hub>=0.30.0,<1.0".

torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.60 GiB

Fires at the VAE decode, after all 50 denoising steps have finished, which is the cruellest possible moment. Check the tail of the message: if it reports a large “reserved but unallocated” figure, the card has the memory and the allocator has fragmented it. Set PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True and rerun. On this box that alone was the difference between a crash and a finished clip.

ValueError: infer_schema(func): Parameter stride has unsupported type list[int]

A ComfyUI startup failure, not a Wan one. Recent ComfyUI pulls in comfy_kitchen, whose custom CUDA ops annotate arguments with builtin generics that older Torch releases cannot turn into an operator schema. Upgrading Torch inside the ComfyUI virtual environment clears it. This is the main argument for giving ComfyUI its own environment instead of sharing the one Wan runs in, since Wan pins dependencies that ComfyUI has already moved past.

What fits on the card you own

Two rows here were measured on the test box. The rest are sized from the quantized file weights plus the roughly 7 GB the fp8 text encoder and VAE add, which is arithmetic rather than a benchmark, so treat them as a starting point and expect to drop a quantization level if a run dies at the decode.

CardRealistic targetBasis
8 GBTI2V-5B at Q4_K_M, or the 14B at Q2_K with offloadingFile sizes, untested here
12 GBT2V-A14B at Q3_K_M or Q4_K_M, 480pFile sizes, untested here
16 GBT2V-A14B at Q4_K_M, 480pMeasured: 14,907 MiB peak
24 GBT2V-A14B at Q6_K or Q8_0, or TI2V-5B at 720pMeasured: 24,079 MiB peak at 720p
48 GBTI2V-5B at 720p with room to work, 14B at Q8_0Extrapolated
80 GBAnything, at bf16, without the offload flagsProject documentation

Three things are worth carrying away from the measurements. The official 24 GB claim is technically true and practically fragile, holding only on a headless card with the expandable-segments allocator and under 500 MiB to spare. The 5B model has no lower resolution to retreat to, so when it does not fit there is no knob to turn. And the quantized 14B, the model the documentation reserves for data-center hardware, was the more comfortable of the two on the same 24 GB card.

For image-to-video rather than text-to-video, the same command takes an --image argument and the same VRAM behaviour applies. Sizing a card for this kind of work follows the same logic as picking a GPU for local models, with one difference worth planning around: video decode allocates in far larger contiguous blocks than language model inference does, so headroom matters more than the parameter count suggests.

Keep reading

Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) AI Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) Open Source LLM Comparison Table (2026) AI Open Source LLM Comparison Table (2026) OpenCode CLI Cheat Sheet: Commands, Sessions and Fixes (PDF) AI OpenCode CLI Cheat Sheet: Commands, Sessions and Fixes (PDF) Best Laptops for Local LLMs: RTX 50 vs 128GB Unified Memory AI Best Laptops for Local LLMs: RTX 50 vs 128GB Unified Memory Black Friday Mini PC Deals: Proxmox, Local AI and Home Assistant AI Black Friday Mini PC Deals: Proxmox, Local AI and Home Assistant Best AWS AI Practitioner and ML Engineer Books for AIF-C01 and MLA-C02 AI Best AWS AI Practitioner and ML Engineer Books for AIF-C01 and MLA-C02

Leave a Comment

Press ESC to close