Articles · · updated

How to Set Up a Virtual Camera on Linux with v4l2loopback

Install the module, pick the options browsers accept, make it survive reboots and kernel updates, and fix apps that cannot see the device.

We make CamLooper, one of the tools this article recommends. Why we write these guides.

Every virtual camera on Linux ends at the same place: v4l2loopback, an out-of-tree kernel module that creates a /dev/video* node one program can write frames into while another reads them as a camera. OBS uses it, browsers read from it, and so does every app that offers a fake cam on Linux.

It is also the part that breaks. The module itself is solid; the trouble is options, persistence and sandboxing. Here is the whole picture.

If you just want a working camera, installing CamLooper from the .deb or .rpm pulls the module in and loads it with the right options during install — you can skip to the troubleshooting sections below if something still misbehaves.

Install the module

Use the DKMS package, not a one-off build, so it rebuilds itself when your kernel updates:

sudo apt install v4l2loopback-dkms v4l2loopback-utils v4l-utils

On Fedora and RHEL the packages live in RPM Fusion:

sudo dnf install akmod-v4l2loopback v4l-utils

Arch: sudo pacman -S v4l2loopback-dkms v4l-utils (the ArchWiki page is the best reference for the module generally). On any distro you also need the kernel headers for your running kernel, or DKMS has nothing to build against.

Which v4l2loopback options do I need?

This is where most setups go wrong. The defaults produce a device that works for some consumers and is invisible to others:

sudo modprobe v4l2loopback devices=1 video_nr=10 card_label="Virtual Camera" exclusive_caps=1
OptionWhat it does
devices=1How many loopback devices to create. One is almost always enough.
video_nr=10Fixes the node at /dev/video10 instead of whatever number is free, so it does not move when you plug in a real webcam.
card_labelThe name apps show in their camera dropdown. Without it you get an unhelpful default.
exclusive_caps=1The important one. Makes the device advertise capture capability only when something is producing frames.

Why does Chrome not see my virtual camera?

By default a loopback device reports that it can both capture and output. Chrome, Chromium, Electron apps and anything built on WebRTC reject a device that claims both — they are looking for a capture device and treat the dual-capability node as something else. The symptom is precise and confusing: the camera appears in v4l2-ctl --list-devices and works in VLC and ffplay, but Google Meet's dropdown does not list it at all. Set exclusive_caps=1 and it appears.

How do I make the virtual camera survive a reboot?

A modprobe lasts until reboot. Two files make it permanent.

/etc/modules-load.d/v4l2loopback.conf — load at boot:

v4l2loopback

/etc/modprobe.d/v4l2loopback.conf — the options to load it with:

options v4l2loopback devices=1 video_nr=10 card_label="Virtual Camera" exclusive_caps=1

Reboot, then confirm both the module and the node:

lsmod | grep v4l2loopback
v4l2-ctl --list-devices

Your loopback device should be listed with the label you chose.

Secure Boot will refuse it

With Secure Boot enabled, the kernel loads only signed modules, and a DKMS-built out-of-tree module is unsigned. modprobe fails with Key was rejected by service, or the module is silently absent after a kernel update.

Two ways out. Either enrol a Machine Owner Key and sign the module — on Debian and Ubuntu, sudo mokutil --import /var/lib/shim-signed/mok/MOK.der, then complete the enrolment in the blue MOK Manager screen on the next boot — or disable Secure Boot in your firmware. Enrolling a key is the better answer if you also run other DKMS modules such as NVIDIA's.

Kernel updates

The single most common way a working setup stops working: you update the kernel, DKMS fails to rebuild, and the camera is gone after the reboot. Check and rebuild:

dkms status
sudo dkms autoinstall

If the build fails, it is nearly always missing headers (linux-headers-$(uname -r)) or a v4l2loopback version older than your kernel. Newer kernels change internal V4L2 APIs, so an old module version simply will not compile against them — update the package first.

Permissions

Video device nodes belong to the video group. Check you are in it:

groups

If not: sudo usermod -aG video $USER, then log out and back in. A new group membership does not apply to your existing session, which is why this looks like it did not work.

The device exists but one app cannot see it — why?

Work through these in order:

  • Chrome or an Electron app. Almost always exclusive_caps=1. Set it, reload the module, and restart the browser completely — Chrome caches its device list.
  • Firefox. More tolerant, but it enumerates devices per page load. Reload the tab after the camera starts.
  • A Flatpak app. Sandboxed apps need explicit device access. Check with flatpak info --show-permissions <app-id> and grant it: flatpak override --user --device=all <app-id>. Flatpak Chrome and Zoom both hit this.
  • A Snap app. Snaps need the camera interface connected: snap connect <snap>:camera.
  • Nothing sees it. Confirm something is actually writing frames — a loopback node with no producer exists but yields nothing, and some apps hide a device that produces no frames. Test with ffplay /dev/video10.

Writing frames into it by hand

Useful for confirming the plumbing works independently of any app:

ffmpeg -stream_loop -1 -re -i clip.mp4 -vf "scale=1280:720,format=yuv420p" -f v4l2 /dev/video10

ffmpeg's V4L2 capture guide documents the device options in both directions.

If that produces a working camera in your browser, the module and the device are fine and any remaining problem is in the app — carry on in the general troubleshooting guide. Note what this command is not: -stream_loop tears the input down and restarts it at each pass, so you get a visible hitch every loop. It is a diagnostic, not a way to run a call — a purpose-built app decodes the next pass before the current one ends, which is what makes the join invisible.

Cleaning up

To unload the module, first close everything using the device, then:

sudo modprobe -r v4l2loopback

If that reports the module is in use, find the culprit with sudo fuser -v /dev/video10. A browser tab that once had camera permission is the usual holder.