Blog

Intel Quick Sync (VAAPI) on a Linux VPS: ffmpeg commands that work

Copy-paste ffmpeg VAAPI commands for an Intel UHD 770 on Linux: vainfo, H.264/HEVC encode (classic and low_power), 10-bit HEVC, GPU transcode with scale_vaapi, AV1 decode, GPU monitoring.

Intel Quick Sync (VAAPI) on a Linux VPS: ffmpeg commands that work

Intel Quick Sync is the video engine inside Intel GPUs. On Linux, ffmpeg drives it through VAAPI (h264_vaapi, hevc_vaapi, scale_vaapi…). This page is a cheat sheet of commands we run on an Intel UHD 770; the encode, transcode and AV1 commands are the ones used in our shared-GPU load test, where they ran for 20 minutes without a single failure.

On our VMs, ffmpeg and the VAAPI drivers are preinstalled and the GPU is the render node /dev/dri/renderD128. On another machine, adjust the device path.

What the UHD 770 can do in hardware

CodecDecodeEncode
H.264yesyes (h264_vaapi)
HEVC 8-bit and 10-bityesyes (hevc_vaapi, -profile:v main10 for 10-bit)
VP9yesyes
AV1yesno: decode only

1. Check the device

ls -l /dev/dri/                       # renderD128 should be listed
vainfo --display drm --device /dev/dri/renderD128

In the vainfo output, each line pairs a profile with an entrypoint:

  • VAEntrypointVLD: hardware decode;
  • VAEntrypointEncSlice: classic hardware encode;
  • VAEntrypointEncSliceLP: low-power encode (-low_power 1 in ffmpeg).

For AV1 (VAProfileAV1Profile0) you will only see VAEntrypointVLD: decode, no encode.

If vainfo fails with a permission error as a normal user, add yourself to the render group and log in again:

sudo usermod -aG render "$USER"

2. Test without a video file

ffmpeg can generate a test pattern, handy to check that encoding works before touching real files:

ffmpeg -vaapi_device /dev/dri/renderD128 \
  -f lavfi -i testsrc2=size=1920x1080:rate=30 -t 60 \
  -vf format=nv12,hwupload -c:v h264_vaapi -b:v 8M -f null -

-f null - discards the output: you only measure the encoder. Without -nostats, ffmpeg prints speed=…x while it runs; above 1x means faster than real time.

3. H.264 hardware encode

The input is decoded on the CPU, frames are uploaded to the GPU (hwupload), then encoded by Quick Sync:

ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' \
  -c:v h264_vaapi -b:v 8M -c:a copy output.mp4

Low-power mode, a separate encoder block on the GPU:

ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' \
  -c:v h264_vaapi -low_power 1 -b:v 8M -c:a copy output.mp4

Both modes work on our VMs. If one of them errors out on another machine, check that vainfo lists the matching entrypoint (EncSlice or EncSliceLP).

4. HEVC (H.265) hardware encode

Same pattern, different encoder:

# classic
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' -c:v hevc_vaapi -b:v 6M -c:a copy output.mkv

# low power
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 \
  -vf 'format=nv12,hwupload' -c:v hevc_vaapi -low_power 1 -b:v 6M -c:a copy output.mkv

5. 10-bit HEVC (Main10)

10-bit uses the p010 pixel format and the main10 profile:

ffmpeg -vaapi_device /dev/dri/renderD128 -i input-10bit.mkv \
  -vf 'format=p010,hwupload' \
  -c:v hevc_vaapi -profile:v main10 -b:v 20M -c:a copy output-main10.mkv

The command we used to create a 4K Main10 test file (30 s, 40 Mbit/s):

ffmpeg -vaapi_device /dev/dri/renderD128 \
  -f lavfi -i testsrc2=size=3840x2160:rate=30 -t 30 \
  -vf format=p010,hwupload -c:v hevc_vaapi -profile:v main10 -b:v 40M -y src.mkv

6. Full GPU transcode (what Plex and Jellyfin do)

Decode, resize and encode all on the GPU: frames never go back to system memory, so the CPU stays almost idle.

ffmpeg -vaapi_device /dev/dri/renderD128 \
  -hwaccel vaapi -hwaccel_output_format vaapi -i movie-4k.mkv \
  -vf 'scale_vaapi=w=1920:h=1080:format=nv12' \
  -c:v h264_vaapi -b:v 8M -c:a copy movie-1080p.mkv
  • -hwaccel vaapi -hwaccel_output_format vaapi: decode on the GPU and keep the frames there;
  • scale_vaapi=…:format=nv12: resize on the GPU and convert 10-bit to 8-bit, because H.264 output is 8-bit;
  • add -low_power 1 after h264_vaapi for the low-power encoder.

In our load test, this exact pipeline (4K HEVC 10-bit → 1080p H.264) ran at about 1.3× real time per server while the shared GPU was saturated. On an idle GPU with the low-power encoder, our Jellyfin & Plex guide measured 12× real time for a 4K HDR → 1080p transcode.

7. AV1: hardware decode, HEVC or H.264 output

The UHD 770 decodes AV1 but has no AV1 encoder: av1_vaapi will not work on this GPU. Decode AV1 on the GPU and re-encode to HEVC or H.264:

ffmpeg -vaapi_device /dev/dri/renderD128 \
  -hwaccel vaapi -hwaccel_output_format vaapi -i input-av1.mkv \
  -vf scale_vaapi=w=1920:h=1080 -c:v hevc_vaapi -b:v 6M -c:a copy output-hevc.mkv

If you need AV1 output, encode it on the CPU (for example libsvtav1), which is how we created our AV1 test file:

ffmpeg -f lavfi -i testsrc2=size=3840x2160:rate=30 -t 10 \
  -c:v libsvtav1 -preset 12 -y src.mkv

To measure decode speed alone, send the frames to the null muxer:

ffmpeg -vaapi_device /dev/dri/renderD128 \
  -hwaccel vaapi -hwaccel_output_format vaapi -i input-av1.mkv -f null -

8. Inside Docker

Pass the render node and its group to the container:

docker run --rm -it \
  --device /dev/dri/renderD128 \
  --group-add "$(stat -c %g /dev/dri/renderD128)" \
  -v "$PWD":/work -w /work \
  your-ffmpeg-image ffmpeg -vaapi_device /dev/dri/renderD128 ...

The image needs its own VAAPI driver (Intel media driver) and an ffmpeg built with VAAPI.

9. Check that the GPU is really working

  • nvtop works inside our VMs: GPU load for the VM and per process.
  • intel_gpu_top does not work inside a VM’s virtual GPU: it relies on i915 PMU counters that are not exposed to the VM. It only works on the physical host.
  • gpu-check (preinstalled on our VMs) tests VAAPI, Vulkan, OpenCL and OpenVINO in one command.
  • After a heavy job, look for GPU errors in the kernel log:
journalctl -k -b | grep -iE "gpu hang|context reset|reset.*engine|guc.*fail"

Empty output means no hang was recorded since boot.

Common mistakes

SymptomLikely cause
vainfo or ffmpeg: permission denied on /dev/dri/renderD128user not in the render group (or container started without --group-add)
Impossible to convert between the formatsframes are in system memory but the encoder expects GPU frames: add hwupload (with -vaapi_device)
High CPU during a “GPU” transcode-hwaccel_output_format vaapi missing, or a CPU filter (scale, not scale_vaapi) in the chain
AV1 encode failsexpected: the UHD 770 has no AV1 encoder

Try it

A Linux VM with an Intel UHD 770 inside, hosted in Germany: ffmpeg, VAAPI, OpenCL and nvtop preinstalled, root SSH, dedicated IPv6. Plans from €14.99 excl. VAT/month, flat price.