Complete Plex, Sonarr, Radarr, Prowlarr, SABnzbd, and Tdarr Setup for Beginners

This is the missing Day 0 setup guide for the Plex, Arr, and Tdarr homelab series. The deeper articles explain architecture, storage, Prowlarr, Sonarr, Radarr, SABnzbd, Tdarr, GPU sharing, reverse proxying, backups, and monitoring. This one answers the first practical question: what do I install, in what order, what paths do I use, and how do I know it worked?

The goal is a clean beginner-safe stack on Ubuntu using Docker Compose, a shared /data layout, predictable permissions, private admin access, Plex playback, Sonarr and Radarr imports, SABnzbd categories, and Tdarr only after the basic workflow is proven.

Rights, lawful use, and scope: This article is for administering a private media server, organizing media you own or are authorized to use, preserving backups, and testing your own infrastructure. It is not legal advice and it is not a guide to finding, acquiring, sharing, or bypassing access controls for copyrighted works. Do not use Plex, Sonarr, Radarr, Prowlarr, SABnzbd, Tdarr, lists, indexers, or download clients to download, store, process, or distribute content unless you have the legal right and service permission to do so.

Safe default: Build this LAN-only or VPN-only first. Do not port-forward Sonarr, Radarr, Prowlarr, SABnzbd, Tdarr, NAS admin pages, or reverse-proxy admin pages. These are control-plane apps that can reveal secrets, change libraries, trigger jobs, delete files, or expose host paths.

What You Are Building

Prowlarr finds and tests lawful sources. Sonarr and Radarr decide what belongs in the TV and movie libraries. SABnzbd handles authorized download-client jobs and reports completion. Sonarr and Radarr import completed items into final library folders. Tdarr optimizes final imported files after they are stable. Plex serves the final media. Backups make the stack rebuildable.

Interactive setup flow
Build the Stack in This Order

Click each step to see what must be true before moving on. The goal is one clean authorized import before broad automation.

  1. 1. FoundationUbuntu + Docker

    Patch the host, install Docker or native packages, and confirm the server can reboot cleanly.

  2. 2. Paths/data Layout

    Create shared download, media, and cache paths before apps start creating files with mismatched ownership.

  3. 3. SourcesProwlarr

    Add only lawful sources and sync indexers outward. Prowlarr finds candidates; it does not decide what belongs in the library.

  4. 4. DownloadsSABnzbd

    Use category folders for movies and TV. Keep incomplete work away from final Plex library roots.

  5. 5. DecisionsSonarr / Radarr

    Set roots, profiles, categories, and one controlled test item before enabling lists or broad searching.

  6. 6. PlaybackPlex

    Point Plex at final media only, then confirm it can see the imported test item without permission fixes.

  7. 7. Optimize LaterTdarr

    Add Tdarr only after imports work. Start with one test file, validate output, then scale carefully.

Rights gateUse these workflows only for media you own or are authorized to store, download, organize, or transcode.
One controlled item firstProve one authorized TV item and one authorized movie item before lists, GPU tuning, or library-wide Tdarr.
Private control planeAdmin UIs and app APIs should stay LAN, localhost, Docker network, or VPN by default.

What We Are Not Building Yet

A complete beginner setup should not start with every advanced feature enabled. The fastest way to make this stack confusing is to enable lists, custom formats, reverse proxy access, GPU transcoding, and Tdarr replacement before a single controlled import works.

Not YetWhy It WaitsWhen to Add It
Radarr import listsLists can add many items quickly.After one manual Radarr import succeeds and you understand tags, root folders, and search-on-add.
Complex custom formatsScoring rules can reject everything or upgrade forever if misunderstood.After basic Sonarr/Radarr searches and imports work.
Reverse proxy hostnamesA proxy can expose admin apps before authentication and access lists are correct.After LAN-only access, app authentication, and backups are proven.
Library-wide Tdarr processingTdarr replaces files; bad settings can affect a whole library.After one test file validates cleanly and Plex/Arr refresh works.
GPU tuningDriver and container GPU issues add another troubleshooting layer.After the stack works without GPU-specific assumptions.
Automatic updatesAn update can migrate databases or change behavior.After backups and rollback notes exist.
AppPlain-English JobBeginner Rule
PlexPlayback and library presentation.Point Plex only at final media folders, not download or cache folders.
ProwlarrIndexer management and testing.Add one source first, test it, then sync it to Sonarr/Radarr.
SABnzbdDownload, repair, unpack, and completion reporting.Use categories so Sonarr and Radarr know which completed job belongs to which app.
SonarrTV intent, monitoring, imports, and upgrades.Use final TV root folders only. Downloads are not root folders.
RadarrMovie intent, root folders, lists, imports, and upgrades.Use final movie root folders only and add lists only after manual tests pass.
TdarrPost-import file optimization.Install it last and start with one test file, not the whole library.

Before You Touch the Terminal

You need a few decisions and pieces of information before commands are useful. Write them down in a private note. Do not paste real passwords, API keys, Plex tokens, NAS credentials, or provider credentials into public posts, screenshots, GitHub issues, or shared config examples.

DecisionRecommended DefaultWhy
Install styleDocker Compose on Ubuntu Server LTS.One repeatable stack file, persistent config folders, and easier rebuilds.
Access modelLAN-only or VPN-only admin access.Safer than exposing media admin apps directly to the internet.
Path layoutUse one shared /data path.Prevents remote path mapping confusion and keeps imports predictable.
Final media/data/media/movies and /data/media/tv.These are the folders Plex, Sonarr, Radarr, and Tdarr agree on.
Temporary workLocal SSD/NVMe for incomplete downloads and transcode cache.Faster and safer for write-heavy work than a NAS share.
Tdarr timingAfter manual Plex, SABnzbd, Sonarr, and Radarr tests pass.Tdarr mutates files; it should not be first-boot automation.
Reverse proxyLater, after local workflow works.A proxy is not a fix for broken app auth, bad paths, or missing backups.
BackupsBefore broad automation.Config folders are small and rebuild-critical.
  • Ubuntu Server LTS installed, preferably wired Ethernet.
  • A router DHCP reservation or static IP plan for the server.
  • An admin workstation with SSH access to the server.
  • A NAS share or local disk plan for final media.
  • A local disk or SSD/NVMe path for incomplete downloads and transcode cache.
  • A Plex account, and Plex Pass only if you plan to use Plex hardware transcoding features.
  • Legal access to any providers, indexers, or content sources you configure.
  • A backup target such as NAS storage, external disk, or another protected machine.
PlaceholderWhat It MeansExample Format
YOUR_ADMIN_USERThe Ubuntu user you created during install.dan, mediaadmin
YOUR_SERVER_IPThe server IP address on your LAN.192.168.1.50
LAN_CIDRYour LAN network range for firewall rules.192.168.1.0/24
YOUR_NASNAS hostname or IP address.nas.local or 192.168.1.20
YOUR_NAS_USERNAS account allowed to access the media share.A dedicated media-share user, not your main admin if possible.
PLEX_CLAIMTemporary Plex claim token if needed for first setup.Generated from Plex, then removed or left blank after claim.
TZTimezone used by containers and logs.America/Chicago

Find the Server IP and Connect with SSH

SSH is how you administer the server from another computer. If you installed Ubuntu Server with OpenSSH enabled, you can connect from macOS Terminal, Windows Terminal, PowerShell, or any SSH client. First, find the server IP address from the server console.

hostname -I
ip address show

The first command usually prints one or more IP addresses. Pick the LAN address assigned by your router. Then, from your normal computer, connect to the server.

ssh YOUR_ADMIN_USER@YOUR_SERVER_IP

What success looks like: Your terminal prompt should change to the Ubuntu server prompt. If SSH fails, check that the server is powered on, connected by Ethernet, has OpenSSH installed, and is reachable from the same LAN. Reserve this IP in your router before you build the stack so app URLs and firewall rules do not change later.

Linux Survival Commands for This Guide

You do not need to become a Linux expert before building this stack, but you do need to understand the few commands this guide uses repeatedly.

CommandWhat It DoesBeginner Note
pwdPrints the current directory.Use this when you are unsure where you are.
lsLists files and folders.ls -lah shows more detail.
cd /pathChanges directory.cd /opt/media-stack moves into the stack folder.
sudoRuns a command as administrator.Read the command first. Administrator mistakes matter.
nano fileOpens a simple terminal text editor.Save with Ctrl+O, Enter, then exit with Ctrl+X.
systemctl status NAMEShows service health.Useful for Docker and optional media-stack service checks.
journalctl -u NAMEShows service logs.Useful when a service fails to start.
docker compose psShows running containers.Run from /opt/media-stack.
docker compose logsShows container logs.Use --tail=100 to avoid huge output.
pwd
ls -lah
cd /opt
cd ~

What success looks like: You should be comfortable moving between your home directory, /opt, and later /opt/media-stack. If a command says a file or folder does not exist, run pwd and ls before guessing.

Quick Glossary

TermMeaning
Terminal / shellThe command-line window where you type Linux commands.
SSHA secure way to connect from your computer to the Ubuntu server terminal.
sudoRuns a command with administrator privileges. Read the command before pressing Enter.
Docker imageThe packaged application template.
ContainerA running app created from an image.
Docker ComposeA YAML file and command that run several containers as one project.
Bind mountA real host folder made visible inside a container.
PUID / PGIDThe user and group IDs containers use when creating files on the host.
UMASKControls default permissions for new files and folders. 002 is a common shared-media setting.
Root folderThe final library folder Sonarr/Radarr manage. It is not a download folder.
CategoryThe SABnzbd label that tells Sonarr or Radarr which completed job belongs to it.
API keyA secret token one app uses to call another app. Treat it like a password.
CacheTemporary workspace for downloads, repairs, transcodes, and previews. Not final storage.
Direct PlayPlex plays the file as-is.
TranscodePlex or Tdarr converts video or audio into another format.
Reverse proxyA routing layer for web apps. It is not magic security by itself.

Install Ubuntu Packages and Docker Engine

Start with system updates, basic troubleshooting tools, storage helpers, and Docker Engine with the Compose plugin. The Docker install block follows Docker’s current Ubuntu repository pattern.

sudo apt update
sudo apt install -y \
  ca-certificates \
  curl \
  gnupg \
  lsb-release \
  acl \
  jq \
  unzip \
  tar \
  wget \
  sqlite3 \
  mediainfo \
  cifs-utils \
  nfs-common \
  ufw

sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL \
  https://download.docker.com/linux/ubuntu/gpg \
  -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install -y \
  docker-ce \
  docker-ce-cli \
  containerd.io \
  docker-buildx-plugin \
  docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker "$USER"

What success looks like: The command docker compose version should print a Compose version after you log out and back in. The docker group is effectively root-equivalent on the host, so only trusted admin users should belong to it.

docker compose version
docker run --rm hello-world

Create the /data Folder Layout

Use one logical path layout. Downloads are temporary. Media folders are final. Cache folders are temporary workspace. Config folders under /opt/media-stack are rebuild-critical.

/opt/media-stack
  .env
  compose.yml
  plex/config
  prowlarr/config
  sabnzbd/config
  sonarr/config
  radarr/config
  tdarr/server
  tdarr/configs
  tdarr/logs

/data
  downloads/incomplete
  downloads/usenet/movies
  downloads/usenet/tv
  media/movies
  media/tv
  cache/plex
  cache/tdarr

Create a shared media group and directories. The 2775 directory mode sets group inheritance on directories, which helps files created under these paths keep the shared group.

sudo groupadd -f media
sudo usermod -aG media "$USER"

sudo install -d -o "$USER" -g media -m 2775 \
  /opt/media-stack

sudo install -d -o "$USER" -g media -m 2775 \
  /data/downloads/incomplete \
  /data/downloads/usenet/movies \
  /data/downloads/usenet/tv

sudo install -d -o "$USER" -g media -m 2775 \
  /data/media/movies \
  /data/media/tv

sudo install -d -o "$USER" -g media -m 2775 \
  /data/cache/plex \
  /data/cache/tdarr

sudo chgrp -R media /data
sudo chmod -R g+rwX /data
sudo find /data -type d -exec chmod 2775 {} +

Stop and verify: Run ls -ld /data /data/media /data/downloads /data/cache. You should see the media group and writable group permissions. If you already mounted an existing NAS library at /data/media, do not blindly run recursive ownership changes against years of existing data. Fix mount options first.

Mount Storage Safely

If final media lives on a NAS, mount the NAS at /data/media. If final media is local, keep the folder local. Choose one mount method. CIFS/SMB is common with Synology and Windows-friendly NAS setups. NFS is common for Linux-first storage.

CIFS / SMB Example

sudo tee /root/.smb-media >/dev/null <<'EOF'
username=YOUR_NAS_USER
password=YOUR_NAS_PASSWORD
domain=WORKGROUP
EOF
sudo chmod 600 /root/.smb-media

MEDIA_GID="$(getent group media | cut -d: -f3)"
CIFS_OPTS="credentials=/root/.smb-media"
CIFS_OPTS="$CIFS_OPTS,uid=$(id -u "$USER")"
CIFS_OPTS="$CIFS_OPTS,gid=$MEDIA_GID"
CIFS_OPTS="$CIFS_OPTS,dir_mode=0775,file_mode=0664"
CIFS_OPTS="$CIFS_OPTS,vers=3.1.1"

sudo mount -t cifs \
  //YOUR_NAS/media \
  /data/media \
  -o "$CIFS_OPTS"

findmnt /data/media
touch /data/media/.media_mount_marker

NFS Example

sudo mount -t nfs \
  YOUR_NAS:/volume1/media \
  /data/media

findmnt /data/media
touch /data/media/.media_mount_marker

After a manual mount works, make it persistent with /etc/fstab or a systemd mount unit. The important beginner rule is this: Plex, Sonarr, Radarr, and Tdarr should not start against an empty local folder that only exists because the NAS failed to mount. Keep the marker file and test it during boot checks.

Create the Media Stack Environment File

The .env file gives Compose a single place for common settings. It should not be published publicly. It may contain values that reveal your timezone, user IDs, Plex claim token, or other private deployment details.

cd /opt/media-stack
MEDIA_UID="$(id -u "$USER")"
MEDIA_GID="$(getent group media | cut -d: -f3)"

cat > .env <<EOF
PUID=$MEDIA_UID
PGID=$MEDIA_GID
TZ=America/Chicago
UMASK=002
UMASK_SET=002
PLEX_CLAIM=
EOF

chmod 600 .env
cat .env

API key handling: Treat Plex tokens, Arr API keys, SABnzbd keys, indexer credentials, NAS credentials, and proxy admin passwords as secrets. Keep them out of screenshots, public posts, Git repositories, shared backup bundles, shell history, and reverse-proxy URLs. Use placeholders in articles and rotate any key that may have appeared in a log, screenshot, or published command.

Create the Docker Compose Stack

This stack intentionally binds most admin apps to 127.0.0.1. That means the apps listen on the server itself, not on every network interface. From another computer, use an SSH tunnel, a VPN, or a carefully protected reverse proxy after the local workflow works. Plex uses host networking because that is common for discovery and LAN playback.

cd /opt/media-stack
cat > compose.yml <<'EOF'
services:
  plex:
    image: lscr.io/linuxserver/plex:latest
    container_name: plex
    network_mode: host
    env_file: .env
    environment:
      - VERSION=docker
      - PLEX_CLAIM=${PLEX_CLAIM:-}
    volumes:
      - /opt/media-stack/plex/config:/config
      - /data/media/movies:/movies
      - /data/media/tv:/tv
      - /data/cache/plex:/transcode
    restart: unless-stopped

  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr
    env_file: .env
    volumes:
      - /opt/media-stack/prowlarr/config:/config
    ports:
      - "127.0.0.1:9696:9696"
    networks: [media]
    restart: unless-stopped

  sabnzbd:
    image: lscr.io/linuxserver/sabnzbd:latest
    container_name: sabnzbd
    env_file: .env
    volumes:
      - /opt/media-stack/sabnzbd/config:/config
      - /data/downloads:/data/downloads
    ports:
      - "127.0.0.1:8080:8080"
    networks: [media]
    restart: unless-stopped

  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    env_file: .env
    volumes:
      - /opt/media-stack/sonarr/config:/config
      - /data:/data
    ports:
      - "127.0.0.1:8989:8989"
    networks: [media]
    restart: unless-stopped

  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    env_file: .env
    volumes:
      - /opt/media-stack/radarr/config:/config
      - /data:/data
    ports:
      - "127.0.0.1:7878:7878"
    networks: [media]
    restart: unless-stopped

  tdarr:
    image: ghcr.io/haveagitgat/tdarr:latest
    container_name: tdarr
    env_file: .env
    environment:
      - serverIP=0.0.0.0
      - serverPort=8266
      - webUIPort=8265
      - internalNode=true
      - inContainer=true
      - ffmpegVersion=7
      - nodeName=MainNode
      - auth=false
    volumes:
      - /opt/media-stack/tdarr/server:/app/server
      - /opt/media-stack/tdarr/configs:/app/configs
      - /opt/media-stack/tdarr/logs:/app/logs
      - /data/media:/media
      - /data/cache/tdarr:/temp
    ports:
      - "127.0.0.1:8265:8265"
      - "127.0.0.1:8266:8266"
    networks: [media]
    restart: unless-stopped

networks:
  media:
    name: media
EOF

Validate the Compose file before starting anything. This catches indentation mistakes, missing variables, and obvious syntax errors.

cd /opt/media-stack
docker compose -f compose.yml config
docker compose -f compose.yml pull

What success looks like: The docker compose config command should print a normalized version of the stack without errors. If YAML fails, fix the file before starting containers.

Start the Apps in a Safe Order

Start the support apps first, then Plex, then Tdarr last. Do not enable lists, broad searches, or library-wide Tdarr processing during first boot.

cd /opt/media-stack
docker compose -f compose.yml up -d prowlarr sabnzbd sonarr radarr
docker compose -f compose.yml ps
docker compose -f compose.yml logs --tail=80 \
  prowlarr sabnzbd sonarr radarr

Use an SSH tunnel from your admin workstation to reach the private localhost-bound web UIs. Replace YOUR_ADMIN_USER and YOUR_SERVER_IP. Keep this terminal open while you configure the apps.

ssh \
  -L 9696:127.0.0.1:9696 \
  -L 8080:127.0.0.1:8080 \
  -L 8989:127.0.0.1:8989 \
  -L 7878:127.0.0.1:7878 \
  -L 8265:127.0.0.1:8265 \
  YOUR_ADMIN_USER@YOUR_SERVER_IP
AppURL From Your Workstation With SSH TunnelFirst Action
Prowlarrhttp://127.0.0.1:9696Set authentication, then add one lawful source/indexer and test it.
SABnzbdhttp://127.0.0.1:8080Set authentication, folders, and categories before connecting Sonarr/Radarr.
Sonarrhttp://127.0.0.1:8989Set authentication and add /data/media/tv as the final TV root.
Radarrhttp://127.0.0.1:7878Set authentication and add /data/media/movies as the final movie root.
Tdarrhttp://127.0.0.1:8265Leave for later until manual imports work.
Plexhttp://YOUR_SERVER_IP:32400/webClaim server, set libraries to final media paths only.

Configure SABnzbd Folders and Categories

SABnzbd should download and unpack into download staging folders. It should not sort directly into Plex library roots. Sonarr and Radarr should import from completed categories into final media folders.

SABnzbd SettingValue
Temporary / incomplete folder/data/downloads/incomplete
Completed download folder/data/downloads/usenet
TV categorytv to /data/downloads/usenet/tv
Movie categorymovies to /data/downloads/usenet/movies
Manual categorymanual, optional, for controlled tests only
SortingDisabled for direct-to-library moves. Let Sonarr/Radarr import.

What success looks like: A TV job should show category tv in SABnzbd, and a movie job should show category movies. Completed files should land under /data/downloads/usenet, not under /data/media.

Connect Prowlarr to Sonarr and Radarr

In Prowlarr, add Sonarr and Radarr as apps using the internal Docker service names. From Prowlarr’s container, http://sonarr:8989 and http://radarr:7878 are the clean internal addresses. Use each app’s API key from its settings page.

Prowlarr AppInternal URLWhat to Test
Sonarrhttp://sonarr:8989Prowlarr app test passes, then synced indexers appear in Sonarr.
Radarrhttp://radarr:7878Prowlarr app test passes, then synced indexers appear in Radarr.

Add one source first, run a test search, and read rejection reasons inside Sonarr/Radarr before adding more. More sources do not fix bad categories, permissions, quality profiles, or path mappings.

Configure Sonarr and Radarr

Sonarr and Radarr need final root folders, SABnzbd as a download client, completed download handling, and conservative starter profiles. Avoid remote path mappings unless apps truly see different paths. With this Compose layout, the apps agree on /data, so remote path mapping should normally not be needed.

AreaSonarrRadarr
Root folder/data/media/tv/data/media/movies
Download clientSABnzbd at http://sabnzbd:8080SABnzbd at http://sabnzbd:8080
SAB categorytvmovies
Completed download handlingEnabledEnabled
Rename filesEnabled after you choose a naming patternEnabled after you choose a naming pattern
Starter profile1080p Balanced TV1080p Balanced Movies

Automation warning: Lists and indexers are intake controls, not permission controls. A list item, search result, or available file is not evidence that you have the right to obtain or process a work. Leave search-on-add disabled for new lists, tag list additions, review them before searching, and remove or exclude items you are not authorized to use.

Configure Plex Libraries

Plex should read final media folders only. Do not add download staging folders, SAB incomplete folders, Tdarr cache, or test output folders as Plex libraries.

Plex LibraryFolder
Movies/movies inside the Plex container, mapped from /data/media/movies on the host.
TV Shows/tv inside the Plex container, mapped from /data/media/tv on the host.
Optional Test LibraryA small controlled folder if you want to test Tdarr behavior before production.

Start Plex after the support apps are stable.

cd /opt/media-stack
docker compose -f compose.yml up -d plex
docker compose -f compose.yml logs --tail=80 plex

Run One Safe End-to-End Test

This is the most important section in the article. Do not skip it. You are proving the workflow before adding custom formats, lists, GPU tuning, reverse proxy access, or Tdarr processing. Use only a selected authorized item that belongs in your library.

  1. In Sonarr, add one test series or episode you are authorized to access.
  2. Run an interactive search and read accepted and rejected results.
  3. Send one selected authorized item to SABnzbd.
  4. Confirm SABnzbd uses category tv.
  5. Confirm Sonarr imports the completed file into /data/media/tv.
  6. Confirm Plex sees the TV item after a scan.
  7. Repeat the same controlled test in Radarr using category movies.
  8. Confirm no manual permission repair, move, or rename was needed.
CheckPass ConditionStop and Fix If
SAB categoryTV uses tv; movies use movies.Jobs land in the wrong completed folder.
ImportSonarr/Radarr import automatically.The item stays in activity or needs manual import every time.
Plex visibilityPlex sees the final imported item.Plex cannot read files or is pointed at the wrong folder.
PermissionsNo manual chmod/chown required.Every import creates unreadable files.
PathsApps agree on /data.Remote path mapping is needed in a single-host stack.

Add Tdarr After Imports Work

Tdarr changes files. That makes it powerful, but it also means it should be added after imports and Plex scans already work. Start with a test library or one controlled file. Do not enable broad library processing on day one.

cd /opt/media-stack
docker compose -f compose.yml up -d tdarr
docker compose -f compose.yml logs --tail=100 tdarr
  • Point Tdarr libraries at final media folders, not incomplete downloads.
  • Use /data/cache/tdarr as local cache.
  • Start with one worker and one test file.
  • Configure the flow to skip compliant files, transcode only when needed, validate output, then replace.
  • After replacement, refresh Plex and rescan Sonarr/Radarr so codec, size, duration, and MediaInfo stay current.
  • Keep Plex priority higher than Tdarr during household viewing hours.

Optional NVIDIA GPU Checks

Only do this on GPU hosts. Install NVIDIA drivers and the NVIDIA Container Toolkit according to your distribution and current vendor guidance. Test the host first, then Docker, then Plex/Tdarr.

nvidia-smi
docker run --rm \
  --gpus all \
  nvidia/cuda:12.5.1-base-ubuntu22.04 \
  nvidia-smi

If that Docker test fails, fix GPU container access before debugging Plex or Tdarr. Once GPU access works, add GPU settings to the specific services that need them and test one Plex hardware transcode plus one Tdarr test job.

Firewall and Remote Access

Docker-published ports can interact with host firewalls in ways beginners do not expect, so the safest first step is to bind admin apps to 127.0.0.1 and use SSH tunnels or VPN. Plex can be allowed on the LAN if you want local playback from clients. Replace the LAN CIDR with your real LAN.

LAN_CIDR="192.168.1.0/24"

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow from "$LAN_CIDR" \
  to any port 32400 \
  proto tcp \
  comment "Plex LAN"
sudo ufw enable
sudo ufw status verbose

VPN note: In this article, VPN means a private administrative access path such as WireGuard, Tailscale, or OpenVPN for reaching your own services. A VPN does not create rights to access, copy, or distribute copyrighted material.

Admin exposure warning: Sonarr, Radarr, Prowlarr, SABnzbd, Tdarr, NAS admin pages, and reverse-proxy admin pages are control-plane apps. Prefer VPN-only access. If you proxy them, require HTTPS, app authentication, strong unique passwords, access lists that allow only LAN/VPN ranges, and logs you actually review.

Optional systemd Control for the Compose Stack

Docker Compose can restart containers by itself, but a systemd unit gives you a familiar service name and a place to enforce mount checks. This is optional. Use it after the stack works manually.

sudo tee /etc/systemd/system/media-stack.service >/dev/null <<'EOF'
[Unit]
Description=Plex Arr Tdarr Docker Compose stack
Requires=docker.service
Wants=network-online.target
After=docker.service network-online.target remote-fs.target
RequiresMountsFor=/data/media /data/downloads /data/cache /opt/media-stack

[Service]
Type=simple
WorkingDirectory=/opt/media-stack
ExecStartPre=/usr/bin/test -f /data/media/.media_mount_marker
ExecStart=/usr/bin/docker compose -f compose.yml up --remove-orphans
ExecStop=/usr/bin/docker compose -f compose.yml down
Restart=on-failure
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now media-stack
sudo systemctl status media-stack

Health Checks, Logs, and Troubleshooting

findmnt /data/media
test -f /data/media/.media_mount_marker
df -h /data/media /data/downloads /data/cache

cd /opt/media-stack
docker compose -f compose.yml ps
docker compose -f compose.yml logs --tail=100 \
  sonarr radarr prowlarr sabnzbd tdarr plex
journalctl -u media-stack -n 100 --no-pager
SymptomLikely CauseWhere to Check
Web UI unreachable from laptopPort is bound to server localhost and no SSH tunnel is open.SSH tunnel command, docker compose ps, service logs.
SAB finishes but Sonarr/Radarr do not importWrong category, wrong completed path, permissions, or path mismatch.SAB categories, Arr activity queue, container mounts.
Plex cannot see mediaWrong Plex library folder or unreadable files.Plex library paths, file permissions, docker logs plex.
Permission denied after importWrong PUID/PGID/UMASK or NAS mount ownership..env, mount options, stat output.
Search results all rejectedQuality profile, language, category, or custom-format rules.Sonarr/Radarr interactive search rejection reasons.
Tdarr replaces file but Arr still shows old sizeArr rescan did not run after replacement.Tdarr post-processing, Arr API health, library rescan.
Everything breaks after rebootNAS did not mount before services started.findmnt, marker file, systemd unit, boot logs.

Back Up Before You Automate

Application config folders are secret-bearing backups. They often contain API keys, tokens, provider settings, private hostnames, and app passwords. Store private backups on restricted or encrypted storage, keep multiple versions, and test restores. Public article bundles, sample compose files, screenshots, and downloadable scripts should use placeholders only.

cd /opt/media-stack
docker compose -f compose.yml stop \
  sonarr radarr prowlarr sabnzbd tdarr plex

tar -czf \
  "/data/media-stack-configs-$(date +%Y%m%d).tgz" \
  /opt/media-stack

docker compose -f compose.yml start \
  sonarr radarr prowlarr sabnzbd tdarr plex

ls -lh /data/media-stack-configs-*.tgz
Back Up ThisWhy
/opt/media-stackCompose file, .env, app config folders, databases, and restore clues.
NAS mount notesNeeded to rebuild /data/media after a host crash.
API placeholders and integration notesDo not publish real keys, but record where they belong.
Tdarr flows/plugins/scriptsThese define how files are mutated and validated.
Plex metadata backup strategyPlex rebuilds are easier when server identity and metadata are protected.

Update and Rollback Routine

Use latest only as a beginner example. Production stacks should pin tested tags or digests once you settle on a working version. Never update every container before a config backup.

cd /opt/media-stack
docker compose -f compose.yml pull sonarr
docker compose -f compose.yml up -d sonarr
docker compose -f compose.yml logs --tail=100 sonarr
docker compose -f compose.yml ps

If an update breaks an app, change the image tag back to the previous known-good tag and restart that one service. If the app database migrated and the older app cannot read it, stop the service and restore the pre-update config backup.

What to Read Next

After this Day 0 setup works, use the rest of the series to tune each area. Do not skip the storage and backup articles; they are what make this stack survivable.

Official References

Final Checkpoint

The stack is ready for deeper tuning when one authorized TV item and one authorized movie item can move through the workflow without manual permission fixes: selected in Sonarr/Radarr, sent to SABnzbd with the right category, completed under downloads, imported into final media, visible in Plex, backed up in config, and left alone by Tdarr until you intentionally test it. That is the baseline. Everything else is optimization.

Need help applying this?

Bring TechGeeks into the real environment.

If you are working through this on a live network, WordPress site, Linux server, AI workflow, or PisoWiFi deployment, send the context and we can help turn it into a practical plan.

Request helpGet field notesRecommended gear

Leave a Reply

Your email address will not be published. Required fields are marked *