GPU
Hardware transcoding
Use your GPU for transcoding. Free and on by default.
When a device can't play a file directly, tofa converts it on the fly. That's the most demanding thing your server does. With a GPU, it happens in hardware: more simultaneous streams, far less CPU, less heat and noise.
Hardware transcoding is free and on by default. If tofa finds a working GPU it uses it, and if a hardware session ever fails mid-stream it falls back to software automatically.
Does my machine qualify?#
| You have | Works with | Rough minimum |
|---|---|---|
| An NVIDIA card | NVENC | GTX 600 series (2012) or newer, driver 470+ |
| An Intel CPU with graphics, or Arc | Quick Sync | 5th gen Core (2014) or newer |
| An AMD GPU | VAAPI | GCN cards (2012) or newer |
| A Mac with Apple silicon | VideoToolbox | any Apple silicon Mac, using the native tofa app |
These are guidance, not gates. tofa doesn't guess from a spec sheet: it runs a real test encode on your GPU and probes which codecs it can decode. Press Test hardware in Settings to see the result for your exact machine, including the reason if something fails.
Running Docker on a Mac or Windows? That works fine as a server, but Docker Desktop runs containers in a virtual machine that can't reach the video hardware, so transcoding stays on the CPU. On a Mac, the native tofa app uses the GPU.
Give the container your GPU#
The compose builder in the
server guide has a hardware
transcoding question that adds the right lines for you. If you already have a
running server, add them to your docker-compose.yml yourself:
Intel or AMD:
services:
tofa:
# ...everything you already have...
devices:
- /dev/dri:/dev/dri
That is all it takes. tofa reads the group that owns the /dev/dri devices and
grants it to itself on startup, so there is no group id to look up.
Two setups still need you to name the group yourself, because tofa cannot work it out from inside the container:
- You pin the container to a fixed user (a
user:line in Compose, or a KubernetessecurityContext). Addgroup_addwith your host's render group: rungetent group renderand use the number it prints. - Your
/dev/dridevices belong torootrather than to a render or video group. tofa will not give itself root's group, so either name a group that can reach the device or fix the ownership on the host.
If you already have a group_add line, leave it. It still works, and it is
used alongside whatever tofa detects.
NVIDIA: install the
NVIDIA container toolkit
on the host once, then hand the GPU to the container. Which snippet you use
depends on your Compose version: check with docker compose version.
Compose v2.30 or newer has the short gpus line:
services:
tofa:
# ...everything you already have...
gpus: all
environment:
NVIDIA_DRIVER_CAPABILITIES: compute,video,utility
Compose v2.29 or older doesn't understand gpus and stops with
Additional property gpus is not allowed. Use the longer form, which does the
same thing:
services:
tofa:
# ...everything you already have...
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
environment:
NVIDIA_DRIVER_CAPABILITIES: compute,video,utility
Either way, keep the NVIDIA_DRIVER_CAPABILITIES line. The video part
unlocks the NVENC encoder; without it, detection finds nothing even though the
GPU is attached.
Then docker compose up -d to recreate the container.
Not using Docker? On a bare Linux install, add the tofa service user to the
render group and restart. On a Mac, the native app needs nothing.
Podman on Fedora, RHEL, or another SELinux system#
Most people can skip this. If you run tofa under Podman on a distribution with
SELinux enforcing (Fedora, RHEL, Rocky, and similar), the Docker snippets above
need a few Podman-specific lines to reach the GPU. Podman does not understand
gpus: all, so hand the card over with a CDI device instead:
services:
tofa:
# ...everything you already have...
devices:
- nvidia.com/gpu=all
security_opt:
- label=disable
environment:
NVIDIA_DRIVER_CAPABILITIES: compute,video,utility
Generate the CDI device once on the host with
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml, and regenerate it
after every driver update or the GPU disappears. The label=disable line lifts
SELinux confinement for the container so it can open the /dev/nvidia* nodes.
It is the blunt option. If you would rather keep the container confined, install
the container toolkit's SELinux policy and swap in
label=type:nvidia_container_t.
Running Podman rootless adds two more lines. group_add: [keep-groups] keeps
the host's render and video groups so the container can reach the card, and
userns_mode: keep-id maps your user into the container so bind-mounted media
and config keep sane ownership:
group_add:
- keep-groups
userns_mode: keep-id
Run tofa as root under Podman and you can leave those last two out.
Check that it works#
Open Settings → Transcoding. The Hardware Transcoding card shows a status pill that reads Active with your backend name once detection finds a working GPU. Press Test hardware to re-run the check any time, for example right after adding the compose lines above.
Under advanced options you can see what was detected, including which codecs your GPU decodes in hardware. Codecs it can't decode still get hardware encoding, which is most of the win.
Play something that needs converting and check the activity panel: the stream shows up as a hardware session.
The settings, briefly#
The defaults are sensible, so you can skip these. If you want the knobs:
- Mode. Auto picks the best working backend. You can force one, or turn hardware off.
- Encoder quality. Speed, balanced, or quality. This keeps working with hardware on.
- HDR tone mapping in hardware. Converts HDR to SDR on the GPU for devices that can't show HDR. Works on every backend, including Macs.
- HEVC full-transcode encoding. Off by default. When a bandwidth cap forces a full conversion, the server can produce HEVC instead of H.264, which keeps more detail at the same bandwidth on devices that support it. A compatible GPU is preferred; on the processor it can be significantly slower.
- Max hardware transcodes. How many GPU sessions to allow at once. Auto sizes this per vendor; when the GPU is full, extra streams use the CPU instead of failing.
- Software fallback. Keep this on. A failed hardware session retries in software invisibly, and a GPU that keeps failing is rested for 30 minutes while everything continues on CPU.
If the test fails#
The card shows the actual error from the encoder, which usually points at the cause:
- Permission denied on /dev/dri: tofa could not give itself the group that
owns the devices. That happens when the container is pinned to a fixed user
(a
user:line, or a KubernetessecurityContext), or when the devices belong torootrather than to a render group. Runls -l /dev/drion the host to see which, then add the owning group withgroup_add. - No such device, or nothing detected at all: the
devices:orgpus:lines are missing, or (NVIDIA) the container toolkit isn't installed on the host. - Additional property gpus is not allowed: your Compose is older than
v2.30. Switch to the
deploy:form in the NVIDIA section above. - Could not open encoder, or a list of encoder settings reported as
unsupported: tofa reached the GPU, and the graphics driver then refused to
start the encoder. That is a driver or firmware problem on the host, not a
tofa setting. On Linux,
vainfoshows what your driver can actually do: if it lists noVAEntrypointEncSliceorVAEntrypointEncSliceLPline for H.264, the encoder is not available to the system and updating the graphics driver package is the first thing to try. Set Mode back to Auto while you work on it, so playback stops attempting a device that isn't working. - Anything else: send us the error text. That output is exactly what we need.