HandBrake Web: a server and worker split for headless transcoding
A self-hosted platform to use HandBrake on your headless devices via a bespoke web interface. Harness the processing power of multiple devices to work on a single queue.
At a glance
- What is it?
- HandBrake Web wraps HandBrakeCLI in a coordinator plus worker model so a queue can be driven from a browser and spread across machines. It is worth adopting only if you accept an early-stage project and can live with its current preset and hardware constraints.
- Who is it for?
- Adopt HandBrake Web if you already run HandBrakeCLI on headless boxes and want one queue spread over several machines, and if you are comfortable with a project whose own README warns it is under heavy development. Do not adopt it if you need AMD VCN encoding, if you expect to build presets in the browser, or if you want a stable interface that will not shift under you.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 173 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap HandBrake Web fills between the desktop app and raw HandBrakeCLI
HandBrake's desktop application is a GUI. HandBrakeCLI is the command line component. Neither gives you a queue that spans several machines. HandBrake Web positions itself in that space: the README describes it as a program for interfacing with HandBrake across multiple machines via a web browser, and it states plainly that the project is not related to or part of the official HandBrake development, using the CLI component under the hood. The audience is people with headless hardware, a NAS, a home server, a spare desktop with a capable GPU, who want to point a browser at a queue instead of opening a terminal on each box. The feature list names the concrete pieces: a web interface, a job queue, bulk job creation for videos in the same directory, preset management, directory watchers that create jobs automatically, distributed encoding, and hardware accelerated encoding through Intel QSV and NVIDIA NVENC. If your workflow is one machine and one file at a time, this is more moving parts than the problem deserves.
Server as coordinator, workers as the only expensive part
The architecture is two roles. The server acts as a coordinator for the workers and also serves the client interface. The README is explicit that the work done by the server is not computationally expensive and that it can run on low-end or low-power devices with no issue. Workers do the encoding through HandBrakeCLI. Jobs are sent to workers by the server, and each worker processes the provided media according to a HandBrake preset configuration. That split is the whole design idea, and it has a practical consequence: the coordinator can sit on a small always-on box while the heavy nodes are switched on only when there is a queue to clear. The README recommends a single worker instance per machine, and that the machine either have a high core-count CPU or have GPU hardware encoding features available to the worker. Running several workers on one machine is not forbidden, but the guidance points the other way, which makes sense because encoding already saturates cores. The queue is the shared state that ties the pieces together; the README's summary line describes using the processing power of multiple devices to work on a single queue.
Getting a server and one worker up with Docker Compose
The README's quick start is a Docker Compose file with two services. The server uses ghcr.io/thenickoftime/handbrake-web-server:latest, publishes port 9999, and mounts two volumes: /path/to/your/data at /data and /path/to/your/media at /video. The worker uses ghcr.io/thenickoftime/handbrake-web-worker:latest and takes three environment variables: WORKER_ID, which the README says should be a unique name; SERVER_URL, set to handbrake-server in the example, with the note to prefix it with http(s):// if setting up a standalone worker; and SERVER_PORT, set to 9999, with the note to change it if a reverse proxy or a different port is in play. Both services run under user: 1000:1000 in the example, with a comment telling you to edit it to a user with permissions to access your media and warning that 0:0 runs as root and is not recommended. The single most important line in the whole file is a comment repeated on both volumes: ensure this path is the same across all containers. Both services mount the media directory at /video, and the worker depends on the server. Get that path wrong on one node and jobs will reference files the worker cannot see. The README points to a Setup Guide wiki page for a detailed walkthrough and to separate wiki pages for presets and hardware acceleration.
Presets come from the desktop app, not from the browser
Presets are the configuration unit for every job, and they are not authored in the web interface. The README states that HandBrake Web currently uses presets configured in the desktop application of HandBrake and exported to .json files, and that exported presets can then be uploaded through the web interface in the Presets section. The current feature list confirms you can upload, rename and delete presets there, while a preset creator is listed under planned features that are not yet implemented. This is a real constraint on workflow: any change to encoding settings has to happen on a machine with the desktop application, then be exported and re-uploaded. For a homelab that is tolerable. For a team that wants to tune settings from a browser, it is a missing piece, and the README does not promise a date for it. Directory watchers are the other automation surface: you create watchers that automatically create jobs based on various criteria, which is how a drop folder turns into a queue without manual job creation.
Hardware encoding support is narrower than the feature list suggests
The README says hardware accelerated encoding needs additional configuration and points to a Hardware Acceleration wiki page. It states that Intel QSV and NVIDIA NVENC are currently supported, and that support for AMD VCN is planned but not yet implemented. If your encoding node has an AMD GPU, the accelerated path is not available to you today, and the README gives no timeline beyond the word planned. Even on supported hardware, the acceleration is not automatic: the extra configuration is a separate step from the base Compose file, so the quick start alone will not light up a GPU. The README also carries a warning that the application is still under heavy development and to use it at your own risk, pointing readers to a Known Issues and Limitations section. Treat that warning literally. This is not a project where you should assume the defaults are safe for unattended operation without reading the wiki pages first.
How it compares with driving HandBrakeCLI from a script or a generic job runner
The obvious alternative is a shell script that loops over files and calls HandBrakeCLI, optionally pushed to other machines over SSH. That approach has no web interface, no persistent queue you can inspect in a browser, no preset upload screen and no directory watcher feature, but it also has no server component to keep running, no shared volume path convention to maintain across containers, and no dependency on a project that describes itself as under heavy development. A general purpose job scheduler such as a cron plus queue arrangement sits somewhere in between: it distributes work, but it does not know what a HandBrake preset is, so preset handling and job creation stay your problem. HandBrake Web's difference is that it models the domain. Jobs, presets, watchers and workers are first class objects in the interface, and the server tracks which worker is doing what. If your queue is a handful of files a week, the script wins on simplicity. If you are feeding several machines from one drop folder and want to see the queue state without logging into each box, the domain model starts to pay for itself.
Licence and the cost of tracking an early-stage project
The repository is licensed AGPL-3.0, and the README links to the LICENSE file. For self-hosted use that is unlikely to change anything, but the AGPL's network clause matters if you ever expose a modified version of this software to other users over a network. That is a general property of the licence, not legal advice, and anyone planning to modify and host it for others should read the licence text and possibly take their own advice. On maintenance: the release history shows v0.8.1 in December 2025, v0.8.0 the day before, and v0.7.3 back in November 2024, so the gap between v0.7.3 and v0.8.0 was roughly a year. The README badges point at a v0.9.0 milestone in development. That cadence means you should expect to pin image tags rather than chase latest, and you should read release notes before upgrading, because a year-long gap between minor versions can carry interface changes. The two components version together as images, so a server upgrade and a worker upgrade are separate actions you have to sequence yourself. Nothing in the supplied material describes a migration path or a compatibility guarantee between server and worker versions, so verify that before mixing versions across machines.
Editorial conclusion
Adopt HandBrake Web if you already run HandBrakeCLI on headless boxes and want one queue spread over several machines, and if you are comfortable with a project whose own README warns it is under heavy development. Do not adopt it if you need AMD VCN encoding, if you expect to build presets in the browser, or if you want a stable interface that will not shift under you. Before committing, verify on your own hardware that your GPU path is one of the two supported ones, confirm every container mounts media at the identical path, and check that your exported preset JSON imports cleanly through the Presets section.
Community notes