Running a media server at home is great — until your storage fills up with 50GB Blu-ray remuxes and you realize there’s no automated way to convert them. That was my situation about a year ago. I had a Jellyfin setup with hundreds of movies and TV shows, and my 8TB NAS was already sitting at 80% capacity. Enter Tdarr.
Tdarr is a distributed transcoding system that scans your media library, applies configurable processing plugins (H.265, AV1, audio normalization, subtitle extraction), and manages the whole pipeline across multiple worker nodes. I’ve been running it in production for about 10 months now, and the results have been consistently stable — I’ve reclaimed over 2TB of space with zero manual intervention.
Quick Start: Get Tdarr Running in 5 Minutes
You’ll need Docker and Docker Compose. Here’s a minimal setup that gets Tdarr server plus one local worker running immediately.
First, create the directory structure:
mkdir -p ~/tdarr/{server,configs,logs,transcode-cache}
Then create docker-compose.yml:
version: "3.8"
services:
tdarr:
image: ghcr.io/haveagitgat/tdarr:latest
container_name: tdarr
restart: unless-stopped
ports:
- 8265:8265 # Web UI
- 8266:8266 # Server port (workers connect here)
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Tokyo
- UMASK_SET=002
- serverIP=0.0.0.0
- serverPort=8266
- webUIPort=8265
- internalNode=true # Run a worker on this same container
- internalNodeID=main-node
- internalNodeName=LocalNode
volumes:
- ~/tdarr/server:/app/server
- ~/tdarr/configs:/app/configs
- ~/tdarr/logs:/app/logs
- ~/tdarr/transcode-cache:/temp
- /path/to/your/media:/media:rw
docker compose up -d
# Web UI available at http://your-server-ip:8265
Open the UI and the built-in worker (LocalNode) should appear online within 30 seconds. That’s it — Tdarr is running.
Deep Dive: How Tdarr Actually Works
Architecture: Server + Nodes
Tdarr splits responsibilities cleanly:
- Server: Scans libraries, maintains a database of files and their processing states, queues transcoding jobs.
- Nodes (Workers): Pick up jobs from the server queue, run FFmpeg or HandBrake, report results back.
The server and nodes communicate over the server port (8266 by default). Nodes can run on completely separate machines — your NAS, an old PC, even a Raspberry Pi for light CPU tasks.
Libraries and Flows
After logging in, go to Libraries and add your media path. Tdarr scans recursively and builds a file list with full codec metadata from each container.
The real power is in Flows — a node-based pipeline editor where each node does one thing: check a codec, check resolution, run FFmpeg, move a file, send a notification. The community library has hundreds of pre-built flows to start from.
A typical H.265 conversion flow looks like this:
- Check if codec is NOT HEVC → if already H.265, skip the file
- Check file size > 500MB → skip small files that aren’t worth the effort
- Run FFmpeg with
-c:v libx265 -crf 22 -preset medium -c:a copy - Check output is smaller than input → if not, revert and mark as not worth converting
- Replace original with transcoded version
The FFmpeg arguments I use for H.265 with all streams preserved:
-c:v libx265 -crf 23 -preset slow -c:a copy -c:s copy -map 0
After assigning the flow to your library, set the node to start processing. Files move through states: Not processed → Queued → Processing → Healthy.
Advanced Usage: Distributed Nodes and GPU Transcoding
Adding a Remote Worker Node
This is where Tdarr earns its place in a HomeLab. I run one worker on my main server and another on an old gaming PC that sits idle most of the day.
On the second machine, deploy just the node image:
version: "3.8"
services:
tdarr-node:
image: ghcr.io/haveagitgat/tdarr_node:latest
container_name: tdarr-node
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Tokyo
- nodeName=GamePC-Node
- serverIP=192.168.1.100 # IP of your Tdarr server
- serverPort=8266
- transcodercputhreads=8
volumes:
- ~/tdarr-node/configs:/app/configs
- ~/tdarr-node/logs:/app/logs
- ~/tdarr-node/transcode-cache:/temp
- /media/library:/media:rw # Must match the path the server uses
The critical requirement: the media path must be accessible from all nodes at the same mount point. I use NFS to mount the NAS media share on every machine at /media/library. This keeps Tdarr’s file paths consistent across the whole cluster.
# On each worker machine, mount NFS share
sudo mount -t nfs 192.168.1.50:/mnt/tank/media /media/library
# For persistent mount across reboots, add to /etc/fstab
192.168.1.50:/mnt/tank/media /media/library nfs defaults,_netdev 0 0
GPU Transcoding with NVIDIA
If your server has an NVIDIA GPU, you can dramatically speed things up with NVENC. Install the NVIDIA Container Toolkit first:
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt update && sudo apt install -y nvidia-container-toolkit
sudo systemctl restart docker
Then add GPU access to your Tdarr compose service:
services:
tdarr:
image: ghcr.io/haveagitgat/tdarr:latest
# ... existing config ...
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
In your Flow, switch to NVENC arguments:
# NVENC H.265 — much faster, slightly larger than CPU slow preset
-c:v hevc_nvenc -preset p4 -cq 24 -c:a copy -c:s copy -map 0
GPU transcoding runs 5–10x faster than CPU for 1080p content. I use GPU for TV show episodes (high volume, many small files) and CPU slow preset for movies where the extra compression quality is worth the time.
Practical Tips from Running This in Production
Test Your Flow Before Mass Processing
Always test on 3–5 files first. Set the library to manual mode, pick a few test files, and watch the transcode log carefully. Verify output quality and file size before pointing the flow at your entire library.
Protect Against Bad Transcodes
Add a size check node immediately after the FFmpeg transcode step:
- If output > 95% of input size → revert to original, mark as not worth converting
- If FFmpeg exits with non-zero code → Tdarr automatically reverts the file
Tdarr keeps the original file intact until it confirms the transcode succeeded, so data loss risk is low — but that size check catches the cases where a file actually got bigger after transcoding (it happens more than you’d expect with already-compressed sources).
Schedule Transcoding During Off-Hours
Nodes can be paused and resumed via the API. I schedule this with cron to avoid chewing CPU during the day:
# Pause node at 8 AM (work hours start)
0 8 * * * curl -s -X POST "http://192.168.1.100:8265/api/v2/update-node" \
-H "Content-Type: application/json" \
-d '{"nodeID": "main-node", "paused": true}' > /dev/null
# Resume node at 10 PM
0 22 * * * curl -s -X POST "http://192.168.1.100:8265/api/v2/update-node" \
-H "Content-Type: application/json" \
-d '{"nodeID": "main-node", "paused": false}' > /dev/null
Watch Your Transcode Cache Disk Space
The /temp volume can fill up fast. Tdarr writes the complete output file there before replacing the original, so for a 20GB 4K file you need 20GB+ free in cache. Allocate a dedicated SSD partition if you can:
# Check cache usage
df -h ~/tdarr/transcode-cache
# Tdarr cleans up on completion, but clean manually after a crash
find ~/tdarr/transcode-cache -name "*.tmp" -mtime +1 -delete
CRF Settings That Actually Work
After months of tuning, here’s where I’ve landed:
- Anime / animation: CRF 20 — flat color areas compress incredibly well with H.265
- Live action 1080p: CRF 23 — typically 40–60% size reduction, quality indistinguishable
- 4K HDR: CRF 20 with
-x265-params hdr-opt=1:repeat-headers=1to preserve HDR metadata - Old DVD rips (480p): Skip — already small enough, not worth the processing time
Running Tdarr across a distributed setup has genuinely transformed how I manage storage. The initial setup takes maybe 30 minutes, but once it’s running you forget it exists — it quietly converts files in the background while you sleep. My 8TB NAS that was at 80% capacity is now sitting at 55%, and the library keeps growing. That kind of set-and-forget automation is exactly what makes HomeLab worth building.

