How to get WebGPU in Headless Chrome on Cloud GPUs

Introduction
I got WebGPU working in headless Chrome with a real NVIDIA GPU in a container (no display).
Tested and verified on RunPod (A40, driver 570), Google Colab (T4, driver 580), and Modal (T4, driver 580).
Struggled on this for about ~6 hours. Had to fix bunch of undocumented issues. ๐ซฉ
Results
| Platform | GPU | Driver | Adapter Found | Compute Shader |
| RunPod | A40 | 570 | 3/3 consistent | not tested (SSH corruption) |
| Colab | T4 | 580 | 3/3 consistent | [1,2,3,4,5,6,7,8] โ [2,4,6,8,10,12,14,16] with 18 features |
The 4 Problems and Their Fixes
Problem 1: Vulkan loader too old
Symptom: vulkaninfo shows ERROR_INCOMPATIBLE_DRIVER or Could not get vkCreateInstance.
Cause: Ubuntu 22.04 ships Vulkan loader 1.3.204. NVIDIA drivers 570+ declare Vulkan 1.4. The old loader rejects the newer driver entirely.
Fix:
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | apt-key add -
wget -qO /etc/apt/sources.list.d/lunarg-vulkan.list \
https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
apt update -qq && apt install -qqq libvulkan1
Verify: vulkaninfo --summary should show your GPU (e.g. Tesla T4 or NVIDIA A40) with Vulkan 1.4.x.
Problem 2: Missing NVIDIA Vulkan ICD
Symptom: vulkaninfo says no vulkan icd or no GPU devices found.
Cause: The container has libnvidia-gl-525 installed but the actual driver is 570/580. The ICD package must match the running driver version.
Fix:
# Check your driver version first
nvidia-smi | head -3
# Install matching package (replace 580 with your version)
apt install -qqq libnvidia-gl-580
Note: If this fails with Invalid cross-device link (RunPod), the driver libs are host-mounted and can't be replaced. In that case the ICD may already exist at /etc/vulkan/icd.d/nvidia_icd.json โ check with cat /etc/vulkan/icd.d/*.json.
Problem 3: navigator.gpu is undefined
Symptom: navigator.gpu returns undefined in page.evaluate().
Cause: Two things:
WebGPU requires a secure context.
about:blank(Puppeteer's default page) is NOT a secure context.http://localhostIS.Puppeteer silently injects
--use-angle=swiftshader-webglwhich overrides your--use-angle=vulkan.
Fix:
// 1. Start a localhost server
const http = require("http");
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end("ok");
});
server.listen(8080, "127.0.0.1");
// 2. Override Puppeteer's default ANGLE flag
const browser = await puppeteer.launch({
headless: "new",
ignoreDefaultArgs: ["--use-angle=swiftshader-webgl"],
args: [
/* your flags */
],
});
// 3. Navigate to localhost BEFORE checking navigator.gpu
const page = await browser.newPage();
await page.goto("http://127.0.0.1:8080");
Also start dbus before launching Chrome:
/etc/init.d/dbus start
Problem 4: requestAdapter() returns null
Symptom: navigator.gpu exists, but requestAdapter() returns null.
Cause: Chrome's WebGPU engine (Dawn) has its own GPU blocklist separate from Chrome's --ignore-gpu-blocklist flag. Dawn rejects NVIDIA drivers 570+ by default.
Fix: Disable Dawn's adapter blocklist:
--enable-dawn-features=allow_unsafe_apis,disable_adapter_blocklist
--disable-dawn-features=disallow_unsafe_apis
Also use powerPreference: 'low-power' in the adapter request (the default preference returns null on some setups):
const adapter = await navigator.gpu.requestAdapter({
powerPreference: "low-power",
});
The Complete Working Setup
System setup (run once)
# 1. Install Chrome
apt update && apt install -y wget
wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome-stable_current_amd64.deb
# 2. Install matching NVIDIA GL/Vulkan ICD (replace version as needed)
apt install -qqq libnvidia-gl-580 # or 570, match nvidia-smi output
# 3. Upgrade Vulkan loader to 1.4+
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | apt-key add -
wget -qO /etc/apt/sources.list.d/lunarg-vulkan.list \
https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
apt update -qq && apt install -qqq libvulkan1
# 4. Start dbus
/etc/init.d/dbus start
# 5. Verify Vulkan sees your GPU
vulkaninfo --summary 2>&1 | grep -A3 "GPU0"
# 6. Install Node + Puppeteer
npm install puppeteer-core
JavaScript (the exact code that works)
import puppeteer from "puppeteer-core";
import { createServer } from "http";
// Localhost server for secure context
const server = createServer((req, res) => {
res.writeHead(200);
res.end("ok");
});
await new Promise((r) => server.listen(8080, "127.0.0.1", r));
const browser = await puppeteer.launch({
executablePath: "/usr/bin/google-chrome-stable",
headless: "new",
ignoreDefaultArgs: ["--use-angle=swiftshader-webgl"],
args: [
"--no-sandbox",
"--headless=new",
"--enable-unsafe-webgpu",
"--enable-features=Vulkan",
"--use-angle=vulkan",
"--disable-vulkan-surface",
"--ignore-gpu-blocklist",
"--disable-gpu-sandbox",
"--enable-dawn-features=allow_unsafe_apis,disable_adapter_blocklist",
"--disable-dawn-features=disallow_unsafe_apis",
],
});
const page = await browser.newPage();
await page.goto("http://127.0.0.1:8080");
const result = await page.evaluate(async () => {
if (!navigator.gpu) return "no gpu";
const adapter = await navigator.gpu.requestAdapter({
powerPreference: "low-power",
});
if (!adapter) return "no adapter";
return (
"FOUND: features=" +
adapter.features.size +
" fallback=" +
adapter.isFallbackAdapter
);
});
console.log(result);
await browser.close();
server.close();
Chrome Flags Explained
| Flag | Why |
--no-sandbox | Required in containers (running as root) |
--headless=new | New headless mode (not legacy) |
--enable-unsafe-webgpu | Enable WebGPU on Linux |
--enable-features=Vulkan | Enable Vulkan graphics backend |
--use-angle=vulkan | Tell ANGLE to use Vulkan |
--disable-vulkan-surface | Disable swapchain (no display needed, offscreen only) |
--ignore-gpu-blocklist | Ignore Chrome's GPU blocklist |
--disable-gpu-sandbox | Disable GPU process sandbox in container |
--enable-dawn-features=allow_unsafe_apis,disable_adapter_blocklist | THE KEY FLAG. Disables Dawn's own separate blocklist that rejects newer NVIDIA drivers |
--disable-dawn-features=disallow_unsafe_apis | Companion to above |
Puppeteer Gotchas
ignoreDefaultArgs: ['--use-angle=swiftshader-webgl']โ Puppeteer silently injects this flag which forces SwiftShader and overrides your Vulkan flag. You MUST exclude it.Navigate to
http://localhostbefore checkingnavigator.gpuโ WebGPU requires a secure context.about:blankis not one.http://localhostis.Use
powerPreference: 'low-power'inrequestAdapter()โ The default preference returns null on some setups.Start dbus โ Chrome expects dbus to be running.
/etc/init.d/dbus startbefore launching.
The Stack
Your Three.js code
โ WebGPU API (navigator.gpu)
โ Chrome's Dawn engine
โ Vulkan
โ NVIDIA driver
โ GPU hardware
What Does NOT Work
Browserbase โ No GPU. Intentionally blocked. SwiftShader only.
Browserless self-serve โ No GPU on standard plans. SwiftShader only.
AWS Lambda โ No GPU available.
Vercel serverless โ No GPU available.
Any container with mismatched Vulkan loader + NVIDIA driver versions โ Loader must support the Vulkan version the driver declares.
Puppeteer default flags without
ignoreDefaultArgsโ SwiftShader override kills Vulkan.about:blankorfile://URLs โ Not secure contexts. WebGPU won't initialize.






