Using HandBrake Web to automate video transcoding

I run HandBrake Web on headless kit to handle video transcoding. This is the setup that has been working for me: a container on a Linux box or NAS, a few mounted folders, and some basic checks before I trust it with anything longer than a test file.

You need Docker or another container runtime, enough CPU and storage for source and output files, and GPU drivers if you plan to use hardware acceleration. Create three directories on the server: config, input and output. The user running Docker needs write access to all three.

docker run -d --name=handbrake-web -p 8080:3000 -v /srv/handbrake/config:/config -v /srv/handbrake/input:/input -v /srv/handbrake/output:/output handbrake-web-server:latest

Check the container is running with docker ps and watch the logs with docker logs -f handbrake-web. The web interface should be available at http://your-server:8080. If you want GPU acceleration, install the correct drivers first and test the vendor tools before turning it on in the web UI. Hardware support varies by CPU and GPU model, so a short transcode test is worth doing before you rely on it.

Open the UI and make sure the input and output paths match the folders you mounted. Create a job by selecting the source file, choosing a preset or custom settings, and pointing the destination at the output path. For general sharing, the usual quick settings are 1080p, H.264, a sensible constant quality value such as RF 20–22, and copied subtitles if needed. Save that as a named preset so you are not rebuilding it every time.

For automation, turn on directory monitoring in the settings and add a watcher for the input folder. Set new files to create jobs automatically and apply the preset you want. I keep separate subfolders for different sources, like /input/phones and /input/camera, so each watcher can use a different preset.

If you want distributed encoding, run worker instances on other machines and register them with the main server. Shared storage is the simpler option: mount the same input and output paths into each worker container, and let the server create jobs while the workers pick them up from the shared filesystem. If you do not want shared storage, run the worker image and point it at the server API endpoint so it can fetch job data and source segments. The firewall still has to allow the management port between server and workers, and any API tokens need to stay out of reach.

I would start with one remote worker, run a few test transcodes, and watch how load moves across CPUs and GPUs before adding more.

After a job finishes, check three places: the web UI job log, the container logs, and the output file properties. Play the output and confirm the duration, audio tracks and subtitles match what you expected. If a transcode fails quickly, check file permissions first; container users not having write access to the output folder causes a lot of these failures. If a transcode is slow, run a single-file software-only test to get a baseline, then switch hardware acceleration on and compare. Watch CPU and GPU use with top, nvidia-smi or intel_gpu_top, depending on the hardware.

For longer runs, rotate logs and back up /config so you keep presets and worker registration if the container is rebuilt.

The short version: map the folders, test one job, then turn on directory watchers and workers once the basics are working.

Related posts

Metadata schema choices for content libraries

Structured metadata only works when it matches how people actually retrieve content. I have seen neat schemas fail as soon as the library meets real records, and tarot makes the problem obvious. If...

Federation trade-offs in self-hosted social feeds

Federation looks tidy until you let it touch the edges, and then the odd cases arrive fast. I prefer self-hosted social feeds that stay explicit about what is local, what is remote, and what should...

FireAvert Z-Wave stove shutoffs for offline safety

FireAvert’s setup does the part that matters without asking Home Assistant to babysit it, which is exactly how I want stove protection to behave. The Home Assistant Z-Wave stove shutoffs badge is...