{"slug": "how-to-use-libvmaf-cuda-on-windows-an-easy-to-follow-guide-wsl2-docker-nvidia", "title": "How to use libvmaf_cuda on Windows: an easy-to-follow guide (WSL2 + Docker + NVIDIA)", "summary": "A developer documented a reliable method for running GPU-accelerated VMAF video quality analysis on Windows using WSL2, Docker Desktop, the NVIDIA Container Toolkit, and a CUDA-enabled FFmpeg via the easyVmaf project. The setup achieved roughly 15x real-time VMAF analysis on an RTX 3060 Mobile, processing a 46-minute video in about three minutes versus roughly an hour on CPU. The guide notes that libvmaf_cuda is NVIDIA-exclusive and will not work with AMD or Intel GPUs.", "body_md": "**TL;DR:** Running GPU-accelerated VMAF on Windows is surprisingly painful. After spending hours fighting broken builds and undocumented errors, the only reliable path I found is **WSL2 + Docker Desktop + NVIDIA Container Toolkit + a CUDA-enabled FFmpeg**. This guide walks you through the whole setup using the [easyVmaf](https://github.com/gdavila/easyVmaf) project, so you can skip the trial-and-error I went through.\n\nThis guide uses **easyVmaf**, a project that ships a `Dockerfile.cuda` specifically built for this purpose. We'll run it inside WSL2, with Docker Desktop and the NVIDIA Container Toolkit handling the GPU passthrough.\n\n**The result:** VMAF running at ~15x real-time speed on an RTX 3060 Mobile. A 46-minute video gets analyzed in about 3 minutes. On CPU, the same task would take like an hour.\n\n⚠️ **AMD and Intel GPUs will not work with this guide.** `libvmaf_cuda` is NVIDIA-exclusive.\n\nIf you've ever tried to calculate VMAF on Windows, you already know that:\n\n`libvmaf` filter works, but it runs on the `libvmaf_cuda` Before starting, make sure you have:\n\n| Component | Minimum requirement | \n|---|---|\n| **Windows** | Windows 10 (version 2004+) or Windows 11 | \n| **NVIDIA GPU** | Any CUDA-capable GPU (I'm using an RTX 3060 Mobile) | \n| **NVIDIA drivers** | Version 525+ (for CUDA 12.x) — [download here](https://www.nvidia.com/Download/index.aspx) | \n| **Disk space** | ~30 GB free (WSL + Docker + images) | \n| **RAM** | 16 GB recommended (WSL2 is memory-hungry) | \n\n⚠️ **Do not install NVIDIA drivers inside WSL.** WSL automatically uses the drivers from Windows. Installing Linux drivers inside WSL will break GPU passthrough.\n\nThis part covers everything needed to get a working Linux + Docker + GPU stack: WSL2, Docker Desktop, and the NVIDIA Container Toolkit.\n\nOpen **PowerShell as Administrator** and run\n\n```\nwsl --install\n```\n\nThis command will:\n\n**Restart Windows** when prompted.\n\nAfter the restart, open PowerShell again and verify:\n\n```\nwsl --list --verbose\n```\n\nExpected output:\n\n```\n  NAME      STATE           VERSION\n* Ubuntu    Running         2\n```\n\nMake sure `VERSION` is `2`. If it says `1`, upgrade with:\n\n```\nwsl --set-version Ubuntu 2\nwsl --set-default-version 2\n```\n\nOpen your Ubuntu terminal either by typing in PowerShell\n\n```\nubuntu\n```\n\nor\n\n```\nwsl\n```\n\nOnce in Ubuntu terminal enter:\n\n```\nsudo apt update && sudo apt upgrade -y\n```\n\nIf you're new to Linux, one of the first things to understand is that WSL doesn't use `C:\\`, `D:\\`, etc. Instead, it mounts every Windows drive under `/mnt/`.\n\n| Windows path | WSL path | \n|---|---|\n| `C:\\Users\\YourName\\Videos` | `/mnt/c/Users/YourName/Videos` | \n| `D:\\Movies` | `/mnt/d/Movies` | \n| `E:\\Backups\\2026` | `/mnt/e/Backups/2026` | \n\nif you already have installed NVIDIA drivers on Windows, run:\n\n```\nnvidia-smi\n```\n\nYou should see the `nvidia-smi` table with your GPU listed. If it doesn't work, your Windows drivers are outdated or WSL isn't configured properly.\n\n```\nWed Sep 16 09:30:35 2026\n+-----------------------------------------------------------------------------------------+\n| NVIDIA-SMI 615.71.08              KMD Version: 616.92        CUDA UMD Version: 13.4     |\n+-----------------------------------------+------------------------+----------------------+\n| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |\n| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |\n|                                         |                        |               MIG M. |\n|=========================================+========================+======================|\n|   0  NVIDIA GeForce RTX 3060 ...    On  |   00000000:01:00.0  On |                  N/A |\n| N/A   55C    P8             14W /  115W |    1085MiB /   6144MiB |      6%      Default |\n|                                         |                        |                  N/A |\n+-----------------------------------------+------------------------+----------------------+\n\n+-----------------------------------------------------------------------------------------+\n| Processes:                                                                              |\n|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |\n|        ID   ID                                                               Usage      |\n|=========================================================================================|\n|  No running processes found                                                             |\n+-----------------------------------------------------------------------------------------+\n```\n\nDownload Docker Desktop from: [https://www.docker.com/products/docker-desktop/](https://www.docker.com/products/docker-desktop/)\n\nDuring installation, make sure to check **\"Use WSL 2 instead of Hyper-V\"**.\n\nOnce installed, open Docker Desktop and go to Settings\n\n**General tab:**\n\n**Resources → WSL Integration:**\n\n**Resources → Advanced (optional):**\n\nIf \"Resource Saver\" is enabled, Docker will suspend WSL2 after inactivity, and you'll have to restart it manually.\n\nIn your Ubuntu terminal:\n\n```\ndocker --version\nDocker version 27.3.1, build ce12230\n```\n\n⚠️ If you get `var/run/docker.sock: connect: permission denied.` jump to the Troubleshooting section.\n\nThis is the component that allows Docker containers to access the GPU.\n\nIn your WSL Ubuntu terminal:\n\n```\ncurl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \\\n  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg && \\\ncurl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \\\n  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \\\n  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list\n\nsudo apt update\nsudo apt install -y nvidia-container-toolkit\nsudo nvidia-ctk runtime configure --runtime=docker\nINFO[0000] Config file does not exist; using empty config\nINFO[0000] Wrote updated config to /etc/docker/daemon.json\nINFO[0000] It is recommended that docker daemon be restarted.\n```\n\nRestart **Docker Desktop** from Windows (either via the restart icon or by quitting and reopening it).\n\nOnce Docker Desktop is running again, execute in your WSL terminal:\n\n```\ndocker run --rm --gpus all nvidia/cuda:12.3.2-base-ubuntu22.04 nvidia-smi\n+-----------------------------------------------------------------------------------------+\n| NVIDIA-SMI 615.71.08              KMD Version: 616.92        CUDA UMD Version: 13.4     |\n+-----------------------------------------+------------------------+----------------------+\n| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |\n| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |\n|                                         |                        |               MIG M. |\n|=========================================+========================+======================|\n|   0  NVIDIA GeForce RTX 3060 ...    On  |   00000000:01:00.0  On |                  N/A |\n| N/A   55C    P8             14W /  115W |    1085MiB /   6144MiB |      6%      Default |\n|                                         |                        |                  N/A |\n+-----------------------------------------+------------------------+----------------------+\n\n+-----------------------------------------------------------------------------------------+\n| Processes:                                                                              |\n|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |\n|        ID   ID                                                               Usage      |\n|=========================================================================================|\n|  No running processes found                                                             |\n+-----------------------------------------------------------------------------------------+\n```\n\nWe'll use the [easyVmaf](https://github.com/gdavila/easyVmaf) project, which ships a `Dockerfile.cuda` configured for GPU-accelerated VMAF.\n\n```\ncd ~\ngit clone --depth 1 https://github.com/gdavila/easyVmaf.git\ncd easyVmaf\n```\n\n💡 **This step is undocumented anywhere else.** I figured it out after spending hours debugging the compilation error with the help of AI. Skip it and your build will fail.\n\nThe original `Dockerfile.cuda` clones the **latest** version of `nv-codec-headers`, which is **incompatible with FFmpeg 8.1**. You'll get this error during the build:\n\n```\nlibavcodec/nvenc.c:2529:42: error: 'NV_ENC_CLOCK_TIMESTAMP_SET' \nhas no member named 'countingType'; did you mean 'countingTypeLSB'?\n```\n\nIn recent versions, NVIDIA renamed `countingType` to `countingTypeLSB/countingTypeMSB`, but FFmpeg 8.1 still uses `countingType`.\n\n**The fix:** Pin `nv-codec-headers` to version `n12.1.14.0`, which still uses `countingType` **and** already includes the modern CUDA functions (`cuStreamCreateWithPriority`, `cuMemHostAlloc`, etc.) that `libvmaf_cuda` needs.\n\nEdit the Dockerfile:\n\n```\nnano Dockerfile.cuda\n```\n\n`CTRL+W`, type `nv-codec-headers`, press `ENTER`. You'll land on this line:\n\n```\nRUN git clone --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git && \\\n    cd nv-codec-headers && \\\n    make install\n```\n\nChange it by adding `n12.1.14.0` before --depth:\n\n```\nRUN git clone --branch n12.1.14.0 --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git && \\\n    cd nv-codec-headers && \\\n    make install\n```\n\nSave with `CTRL+X`, then `Y`, then `ENTER`.\n\n```\ndocker build -f Dockerfile.cuda -t easyvmaf:cuda .\n```\n\n⚠️ The `easyvmaf:cuda` image defines `easyVmaf` (its own CLI) as the `ENTRYPOINT`. To run raw `ffmpeg`, you must override the entrypoint.\n\n```\ndocker run --rm --gpus all --entrypoint ffmpeg easyvmaf:cuda -filters | grep -E \"libvmaf|scale_cuda\"\nphp\n.. libvmaf           VV->V      Calculate the VMAF between two video streams.\n.. libvmaf_cuda      VV->V      Calculate the VMAF between two video streams.\n.. scale_cuda        V->V       GPU accelerated video resizer\n```\n\n**If you see `libvmaf_cuda`, you're done with the setup.**\n\nBy default, `--gpus all` alone only grants the compute and utility capabilities. It does not mount the video decode/encode libraries `libnvcuvid.so.1` (NVDEC) and `libnvidia-encode.so.1` (NVENC). Since we tell FFmpeg to decode with `-hwaccel cuda`, it needs those libraries. Without them, FFmpeg fails with:\n\n```\nCannot load libnvcuvid.so.1\nFailed loading nvcuvid.\nFailed setup for format cuda: hwaccel initialisation returned error.\n```\n\n**The fix:** explicitly request the `video` capability:\n\n```\n--gpus all,capabilities=video\n```\n\nNow Docker mounts `libcuda.so.1` (CUDA compute), `libnvcuvid.so.1` (NVDEC), and `libnvidia-encode.so.1` (NVENC). FFmpeg can then decode, filter, and analyze entirely on the GPU.\n\n```\ndocker run --gpus all,capabilities=video --rm --entrypoint ffmpeg \\\n  -v \"/path/to/your/videos\":/videos \\\n  easyvmaf:cuda \\\n  -hwaccel cuda -hwaccel_output_format cuda \\\n  -i \"/videos/distorted.mkv\" \\\n  -hwaccel cuda -hwaccel_output_format cuda \\\n  -i \"/videos/reference.mkv\" \\\n  -filter_complex \"[0:v]scale_cuda=format=yuv420p[dis];[1:v]scale_cuda=format=yuv420p[ref];[dis][ref]libvmaf_cuda=log_fmt=json:log_path=/videos/vmaf_full.json\" \\\n  -f null -\n```\n\nHere's the output from a test on a 46-minute video file:\n\n```\n[Parsed_libvmaf_cuda_2 @ 0x760b44004f80] VMAF score: 93.558906\nspeed=14.9x elapsed=0:03:05.05\n[out#0/null @ 0x5ef225e22140] video:27438KiB audio:2072848KiB subtitle:0KiB\nframe=66265 fps=350 q=-0.0 Lsize=N/A time=00:46:03.79 bitrate=N/A speed=14.6x elapsed=0:03:09.43\n```\n\n**3 minutes for a 46-minute video at ~15x real-time speed.** That's the whole point of using `libvmaf_cuda`.\n\n`var/run/docker.sock: connect: permission denied`\n\n```\ndocker: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head \"http://%2Fvar%2Frun%2Fdocker.sock/_ping\": dial unix /var/run/docker.sock: connect: permission denied.\n```\n\nYour user doesn't belong to the `docker` group, which owns `/var/run/docker.sock`.\n\nfix:\n\nenter this command in your WSL Ubuntu terminal\n\n```\nsudo usermod -aG docker $USER\n```\n\nThis adds your user to the `docker` group. Then **close and reopen WSL** (group changes only apply to new sessions), and verify:\n\n```\ngroups\nyouruser adm cdrom sudo dip plugdev users docker\n```\n\n`Cannot load libnvcuvid.so.1`\nMissing `,capabilities=video` in the `--gpus` flag. See section 3.1.\n\n`NV_ENC_CLOCK_TIMESTAMP_SET has no member named 'countingType'`\nYou didn't pin `nv-codec-headers` to `n12.1.14.0`. See section 2.2.\n\nEdit `C:\\Users\\YOUR_USER\\.wslconfig`:\n\n```\n[wsl2]\nvmIdleTimeout=-1\n```\n\nThen run `wsl --shutdown` in PowerShell. Also disable \"Resource Saver\" in Docker Desktop (see section 1.3).\n\nSetting up `libvmaf_cuda` on Windows was a long journey. At the start, I couldn't find much information about it — most guides either stop at \"use `libvmaf` on CPU\" or assume you're on Linux. Even though this isn't 100% native to Windows (it runs through WSL2 + Docker), it's a solid alternative that is absolutely worth the effort.\n\nOnce it's working, you get VMAF analysis at **15x real-time speed**, which completely changes what's practical for video quality workflows.\n\nIf you found this post useful and you're looking for a Full Stack Developer or a technical writer, feel free to reach out!\n\nThanks for reading! 🙌", "url": "https://wpnews.pro/news/how-to-use-libvmaf-cuda-on-windows-an-easy-to-follow-guide-wsl2-docker-nvidia", "canonical_source": "https://dev.to/rtagl/-how-to-use-libvmafcuda-on-windows-an-easy-to-follow-guide-wsl2-docker-nvidia-59mc", "published_at": "2026-09-16 19:21:17+00:00", "updated_at": "2026-09-16 19:40:53.245123+00:00", "lang": "en", "topics": ["developer-tools", "ai-infrastructure"], "entities": ["easyVmaf", "NVIDIA", "WSL2", "Docker Desktop", "NVIDIA Container Toolkit", "FFmpeg", "libvmaf_cuda", "RTX 3060 Mobile"], "alternates": {"html": "https://wpnews.pro/news/how-to-use-libvmaf-cuda-on-windows-an-easy-to-follow-guide-wsl2-docker-nvidia", "markdown": "https://wpnews.pro/news/how-to-use-libvmaf-cuda-on-windows-an-easy-to-follow-guide-wsl2-docker-nvidia.md", "text": "https://wpnews.pro/news/how-to-use-libvmaf-cuda-on-windows-an-easy-to-follow-guide-wsl2-docker-nvidia.txt", "jsonld": "https://wpnews.pro/news/how-to-use-libvmaf-cuda-on-windows-an-easy-to-follow-guide-wsl2-docker-nvidia.jsonld"}}