Media
Tdarr logo

How to install Tdarr with Docker

Tdarr Docker install guide cover
Difficulty: IntermediateTime: 30 minutesTested with: Tdarr 2.94 (official image)

Tdarr is a distributed transcoding system for your media library. Point it at your films and TV and it can convert everything to one codec (H.265 saves a lot of disk), remove unwanted audio and subtitle tracks, and health-check files for corruption, all automatically and in the background. The free tier covers everything a homelab needs.

In this guide we will run the Tdarr server, with its built-in node, in Docker using the official image, optionally pass through an Intel or AMD GPU for hardware transcoding, set up a first library, and get it ready to sit behind a reverse proxy.

💡
Note: This guide works with the current Tdarr 2.94 release (2.94.02 at the time of writing) from ghcr.io/haveagitgat/tdarr.

✅ Before you start

  • A Linux host with Docker and Docker Compose v2 installed (docker compose version should work).
  • Your media on a local disk or mounted share, e.g. /path/to/data/media/movies and /path/to/data/media/tv.
  • A fast disk with plenty of free space for the transcode cache (an SSD is ideal); at least the size of your largest file.
  • Optional but recommended: an Intel or AMD GPU for VAAPI/QSV hardware transcoding, or an NVIDIA GPU with the NVIDIA Container Toolkit installed.
  • Important: a backup of anything you cannot replace. Tdarr replaces the original files once a transcode passes.

STEP 1: Find your user and group IDs

Tdarr should run as a normal user, not as root. Log in to your Docker host over SSH and run:

id

You will see something like uid=1000(you) gid=1000(you). Make a note of the two numbers; they become PUID and PGID below.

If you want hardware transcoding, also check the GPU is visible on the host: ls -l /dev/dri should show renderD128.

STEP 2: Create the folders

I like to keep every app's data under one parent folder so backups are a single job. Create the folders for Tdarr, with the transcode cache on your fastest disk:

mkdir -p /path/to/appdata/tdarr/{server,configs,logs}
mkdir -p /path/to/transcode_cache

When you are done, the layout should look like this:

/path/to/appdata/tdarr/
    docker-compose.yml
    server/                       # database, samples and plugins
    configs/                      # Tdarr_Server_Config.json, Tdarr_Node_Config.json
    logs/
/path/to/transcode_cache/         # temporary files while transcoding (fast disk)
/path/to/data/media/
    movies/
    tv/

Folder layout

⚠️
Warning: Using the hardlink layout from the Sonarr and Radarr guides? When Tdarr replaces a file in /data/media, the hardlink to the copy still seeding in /data/torrents is broken and both copies use space until the torrent is removed. Many people only transcode once seeding has finished.

Finally, this guide puts the app on the shared media Docker network used by the rest of my media stack, so other containers can reach it by name. If you followed the Jellyfin guide you already have it; otherwise create it once:

docker network create media

STEP 3: The compose file

Create /path/to/appdata/tdarr/docker-compose.yml and paste in the following. Use the copy button in the top right corner of the block.

services:
  tdarr:
    image: ghcr.io/haveagitgat/tdarr:latest
    container_name: tdarr
    hostname: tdarr
    restart: unless-stopped
    environment:
      - PUID=1000                # your user ID from STEP 1
      - PGID=1000                # your group ID from STEP 1
      - TZ=Europe/London         # your time zone
      - UMASK_SET=002
      - serverIP=0.0.0.0
      - serverPort=8266
      - webUIPort=8265
      - internalNode=true        # run a node inside the server container
      - inContainer=true
      - ffmpegVersion=7
      - nodeName=MyInternalNode
      - auth=false               # see STEP 7 before exposing Tdarr
    volumes:
      - /path/to/appdata/tdarr/server:/app/server
      - /path/to/appdata/tdarr/configs:/app/configs
      - /path/to/appdata/tdarr/logs:/app/logs
      - /path/to/data/media:/media
      - /path/to/transcode_cache:/temp
    devices:
      - /dev/dri:/dev/dri        # Intel/AMD GPU for hardware transcoding
    ports:
      - 8265:8265                # web UI
      - 8266:8266                # server port (for extra nodes)
    networks:
      - media

networks:
  media:
    external: true

docker-compose.yml

💡
Note: No GPU?
Delete the devices: section and Tdarr will transcode on the CPU. NVIDIA? Install the NVIDIA Container Toolkit on the host, add NVIDIA_DRIVER_CAPABILITIES=all and NVIDIA_VISIBLE_DEVICES=all to environment, and add a GPU reservation under deploy: resources: reservations: devices: with driver: nvidia, count: all and capabilities: [gpu], as in the official compose example.

STEP 4: The settings explained

Setting What it does
PUID / PGID The user and group Tdarr runs as. It must be able to read and write your media, because it replaces files.
TZ Time zone, for schedules and logs. List of zones.
UMASK_SET 002 keeps new files group-writable, as in the official example.
serverIP / serverPort / webUIPort Where the server listens inside the container. 8265 is the web UI and 8266 is what extra nodes connect to.
internalNode / nodeName Runs a worker (a "node") inside the server container, so one container does everything. You can add more nodes on other machines later with the tdarr_node image.
ffmpegVersion Which bundled FFmpeg major version the node uses.
auth Turns the login on or off. Off is fine on a trusted LAN; switch it on before exposing Tdarr (see STEP 7).
/app/server Tdarr's database, samples and plugins. Back this up along with /app/configs.
/media Your library. Tdarr reads and rewrites files here.
/temp The transcode cache. Work-in-progress files live here until they pass and replace the originals.
devices Hands the GPU to the container for VAAPI/QSV hardware transcoding.

STEP 5: Start Tdarr

From the folder containing your compose file:

cd /path/to/appdata/tdarr
docker compose up -d
docker compose logs -f

Wait until the logs settle down (press Ctrl+C to stop following them; the container keeps running), then check it is up:

docker ps --filter name=tdarr

To check Docker can use your Intel GPU, run the quick QSV test from the Tdarr docs. It starts a throwaway tdarr_node container and removes it again afterwards:

docker run --rm --device=/dev/dri:/dev/dri ghcr.io/haveagitgat/tdarr_node:latest \
  /bin/bash -e -c 'ffmpeg -hwaccel qsv -f lavfi -i color=c=black:s=256x256:d=1:r=30 -c:v:0 hevc_qsv -f null /dev/null'

If it finishes without errors, hardware encoding is available. (On AMD, the docs have a matching VAAPI test.)

STEP 6: First-run setup

Open http://192.168.1.20:8265 in your browser, replacing the IP with your Docker host's address. The dashboard should show MyInternalNode as connected.

  1. Add a library. Go to the Libraries tab and add one. Set the Source to /media/movies and the Transcode cache to /temp.
  2. Pick what should happen to each file. Under Transcode Options, choose a Flow (the visual editor) or a classic plugin stack. A good first goal is "re-encode to HEVC and drop unneeded subtitle tracks". Start with a small test folder rather than your whole library.
  3. Set the workers. On the dashboard, open your node and set how many GPU and CPU transcode and health check workers it may run at once. One GPU transcode worker is a sensible start.
  4. Scan. Back on the library, run a Scan (Find new). Files appear in the queue and start processing.
  5. Repeat for /media/tv once you are happy with the results.
👍
Tip: Check before you trust it
Watch a few transcoded files from start to finish before letting Tdarr loose on everything. Tdarr's own health checks catch broken files, but your eyes and ears are the real test.

STEP 7: Put Tdarr behind a reverse proxy

Tdarr is an admin tool, so I keep it on the LAN or behind a VPN. If you do want it on a domain, first turn on the login: set auth=true and restart, and Tdarr will ask you to create a username and password in the web UI. Any separate nodes then need an API key (created under Tools → API Keys) in their apiKey variable. Never forward ports 8265 or 8266 from your router.

Option A: Traefik (Docker labels)

If Traefik runs on the same Docker host and is attached to the media network, add these labels to the tdarr service. They assume your HTTPS entry point is called websecure and your certificate resolver is called cloudflare; change them to match your Traefik config.

    labels:
      - traefik.enable=true
      - traefik.http.routers.tdarr.rule=Host(`tdarr.example.com`)
      - traefik.http.routers.tdarr.entrypoints=websecure
      - traefik.http.routers.tdarr.tls.certresolver=cloudflare
      - traefik.http.services.tdarr.loadbalancer.server.port=8265

docker-compose.yml (extra lines)

Option B: Traefik (file provider)

If Traefik runs on a different machine, add a router and service to your dynamic config file instead:

http:
  routers:
    tdarr:
      rule: Host(`tdarr.example.com`)
      entryPoints:
        - websecure
      tls:
        certResolver: cloudflare
      service: tdarr
  services:
    tdarr:
      loadBalancer:
        servers:
          - url: http://192.168.1.20:8265

dynamic.yml

Option C: Nginx Proxy Manager

Add a Proxy Host for tdarr.example.com with scheme http, forward hostname tdarr (if NPM is on the media network) or 192.168.1.20, and forward port 8265. Request a certificate on the SSL tab and tick Force SSL. Turn Websockets Support on.

Only the web UI (8265) goes through the proxy. Extra nodes on your LAN connect straight to port 8266.

Updating Tdarr

The latest tag always follows the newest stable release. To update:

cd /path/to/appdata/tdarr
docker compose pull
docker compose up -d
docker image prune -f
⚠️
Warning: Before a major upgrade: back up /path/to/appdata/tdarr/server and configs, and read the release notes. Update any separate nodes to the same version as the server.

Troubleshooting

Files fail with Permission denied. The PUID/PGID user cannot write to your media. Check with docker exec -it tdarr ls -ln /media/movies and fix ownership on the host.

Hardware transcodes fail but CPU ones work. Make sure /dev/dri exists on the host and is passed through, then run the QSV test from STEP 5. On Proxmox, the iGPU must be passed through to the VM first.

The transcode cache fills the disk. Point /temp at a disk with more space, and lower the number of workers so fewer files are in flight at once.

The node does not appear on the dashboard. Check internalNode=true and look at docker logs tdarr --tail 50. With auth=true, nodes need a valid apiKey.

port is already allocated when starting. Something else is using 8265 or 8266. Change the left-hand side of the port mapping, for example 8275:8265.

That's it! Your library will slowly shrink and tidy itself while you sleep. Keep an eye on that first batch, then let it get on with it. 🎞️