Skip to content

Instantly share code, notes, and snippets.

@gnutix
Last active August 24, 2026 21:16
Show Gist options
  • Select an option

  • Save gnutix/f32f057f40f4a28ad397e36c840fdcb8 to your computer and use it in GitHub Desktop.

Select an option

Save gnutix/f32f057f40f4a28ad397e36c840fdcb8 to your computer and use it in GitHub Desktop.
Install a Raspberry Pi as a torrent download station (09.2019)

Install a Raspberry Pi as a torrent download station (09.2019)

Install Raspbian Lite

  1. Download as zip https://www.raspberrypi.org/downloads/raspbian/ and extract it

  2. Download and install Etcher https://www.balena.io/etcher/

  3. Plug the SD card in the computer, run Etcher and flash the .img on the SD card, remove the SD card, plug it into the RPi, boot

  4. Default login is pi / raspberry. Beware that the default locale is en_UK, which means if you have an (QWERT)Z keyboard layout, you'll need to type "raspberrz" as the password for it to work

  5. Change the keyboard settings : sudo nano /etc/default/keyboard (/ is found on the - key on QWERTZ) and set (something along the lines of) the following (which is my personal setup), then reboot.

    XKBMODEL="pc105"
    XKBLAYOUT="ch"
    XKBVARIANT="fr"
    XKBOPTIONS="ctrl:nocaps,shift:both_capslock"
    
  6. Enable SSH access : sudo raspi-config, "5 Interfacing Options > P2 SSH".

  7. Set a static IP : run ip -4 addr show | grep global to get the "network space" (the number after the / at the end of the IP address). Then sudo nano /etc/dhcpcd.conf and add your static IP address with the correct "network space" (in my case 17. Most often it's 24). Add IPQoS cs0 cs0 on its own line at the very end of /etc/ssh/sshd_config to prevent potential SSH freezes. Finally, reboot.

    interface eth0
    static ip_address=192.168.1.48/17
    static routers=192.168.1.1
    static domain_name_servers=192.168.1.1 
    
  8. Add an SSH alias to your config : sudo nano ~/.ssh/config

    Host *
        PreferredAuthentications publickey,password
        
    Host raspbian
        Hostname 192.168.1.48
        User pi
    
  9. Add your public SSH key to the RPi : cat ~/.ssh/id_rsa.pub | ssh raspbian-wired 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'. Connect using ssh raspbian without password.

  10. Update packages (might take a while - apparently it includes kernel stuff. Grab some snacks) :

    sudo apt-get update
    sudo apt-get upgrade
    
  11. Install speedtest (optional) :

    wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py
    chmod +x speedtest-cli
    

Done !

Install fan (optional)

You can buy this awesome product and follow this installation guide.

Install softwares

  1. Mount the Synology's drive.

    Start by following this guide : https://web.archive.org/web/20250315162557/https://www.junipermonkeys.com/2019/03/making-a-raspberry-pi-downloader-box-work-with-a-synology-when-youre-dumb-and-impatient/ (the original went offline around mid-2025, so this is the Wayback copy), which basically says how to configure the NFS permissions on the Shared Folders. In DSM : Control Panel > Shared Folder > select the folder > Edit > NFS Permissions > Create :

    • Host/IP : 192.168.1.0/255.255.255.0 (add 192.168.0.0/255.255.255.0 as a second rule if your LAN spans both, as mine does)
    • Privilege : Read/write
    • Squash : Map all users to admin (SUPER IMPORTANT !! Otherwise transmission's user can't write in the Downloads folder)

    And enable NFS in "Control Panel > File Services > NFS". Then on the RPi :

    sudo apt-get install ntfs-3g
    sudo mkdir -p /media/scratology/{Downloads,Movies,TVShows}
    

    Add an SSH alias for the Synology : sudo nano ~/.ssh/config

    Host *
        ForwardAgent yes
        ServerAliveInterval 300
        ServerAliveCountMax 10
        StrictHostKeyChecking no
        PreferredAuthentications publickey,password
    
    Host scratology
        Hostname 192.168.1.42
        User <nas-user>
        Port <ssh-port>
    

    Add an entry in the hosts file :

    echo '192.168.1.42 scratology-wired' | sudo tee -a /etc/hosts
    

    Edit sudo nano /etc/fstab with the following :

    scratology-wired:/volume2/Downloads /media/scratology/Downloads nfs defaults,noatime,x-systemd.automount,nolock 0 0
    scratology-wired:/volume2/Movies /media/scratology/Movies nfs defaults,noatime,x-systemd.automount,nolock 0 0
    scratology-wired:/volume2/TVShows /media/scratology/TVShows nfs defaults,noatime,x-systemd.automount,nolock 0 0
    

    Then, sudo mount -av. If it doesn't work, it's probably one of the fstab mounting options... good luck! Finally, run sudo chmod -R 0777 /volume2/{Downloads,Movies,TVShows} on the Synology NAS to ensure we avoid permissions issues.

  2. Install .NET Core 3.1 for Jackett/Radarr install

    wget https://download.visualstudio.microsoft.com/download/pr/349f13f0-400e-476c-ba10-fe284b35b932/44a5863469051c5cf103129f1423ddb8/dotnet-sdk-3.1.102-linux-arm.tar.gz -P /tmp
    wget https://download.visualstudio.microsoft.com/download/pr/8ccacf09-e5eb-481b-a407-2398b08ac6ac/1cef921566cb9d1ca8c742c9c26a521c/aspnetcore-runtime-3.1.2-linux-arm.tar.gz -P /tmp
    sudo mkdir /opt/dotnet-arm32
    sudo tar -xf /tmp/dotnet-sdk-*-linux-arm.tar.gz -C /opt/dotnet-arm32
    sudo tar -xf /tmp/aspnetcore-runtime-*-linux-arm.tar.gz -C /opt/dotnet-arm32
    sudo chown -R pi:pi /opt/dotnet-arm32
    sudo chmod +x /opt/dotnet-arm32/dotnet
    

    Then edit the ~/.profile file to add at the end of it :

    export DOTNET_ROOT=/opt/dotnet-arm32
    export PATH=$PATH:/opt/dotnet-arm32
    

    Finally run source ~/.profile and dotnet --info (from another folder than /opt/dotnet-arm32) to make sure it works.

RPi-Monitor (optional)

sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 2C0D3C0F
echo 'deb http://giteduberger.fr rpimonitor/' | sudo tee /etc/apt/sources.list.d/rpimonitor.list
sudo apt-get update
sudo apt-get install rpimonitor
sudo /etc/init.d/rpimonitor update
sudo /etc/init.d/rpimonitor install_auto_package_status_update

Edit sudo nano /etc/rpimonitor/template/network.conf to uncomment every lines except the following lines that should be commented out :

#web.status.1.content.8.line.1="To activate network monitoring, edit and customize <font color='#AA0000'><b>network.conf</b></font>"
#web.status.1.content.8.line.2="Help is available in man pages:"
#web.status.1.content.8.line.3="<font color='#AA0000'><b>man rpimonitord</b></font> or <font color='#AA0000'><b>man rpimonitord.conf</b></font>"

Restart the service sudo service rpimonitor restart. Access it via http://192.168.1.48:8888/ . If it does not start automatically after ar reboot, add $network at the end of these lines in /etc/init.d/rpimonitor :

- # Required-Start:    $remote_fs $syslog	# Required-Start:    $remote_fs $syslog
+ # Required-Start:    $remote_fs $syslog	# Required-Start:    $remote_fs $syslog $network
- # Required-Stop:     $remote_fs $syslog	# Required-Stop:     $remote_fs $syslog
+ # Required-Stop:     $remote_fs $syslog	# Required-Stop:     $remote_fs $syslog $network

RasPi Check Android app (optional)

You can download RasPi Check's Android app if you want nice dashboards to monitor your PI from your smartphone.

Transmission

sudo apt-get install transmission-daemon
# you need to stop the daemon to be able to edit the config file, otherwise it gets overridden immediately
sudo service transmission-daemon stop
sudo mkdir -p /media/scratology/Downloads/transmission/{incomplete,completed}

Then edit the settings : sudo nano /etc/transmission-daemon/settings.json

    "download-dir": "/media/scratology/Downloads/transmission/completed",
    "incomplete-dir": "/media/scratology/Downloads/transmission/incomplete",
    "incomplete-dir-enabled": true,
    "rpc-whitelist-enabled": false,

Then add the following line in the [Unit] section of : sudo nano /lib/systemd/system/transmission-daemon.service

RequiresMountsFor=/media/scratology/Downloads
After=network-online.target

To prevent UDP warnings when the service starts, add the following lines to a new file /etc/sysctl.d/97-transmission.conf (the numeric prefix controls apply order) :

net.core.rmem_max = 16777216
net.core.wmem_max = 4194304

Then execute sudo systemctl restart systemd-sysctl.

Finally run the service again : sudo service transmission-daemon start. To make sure it will start at boot automatically, run : sudo systemctl enable transmission-daemon.service.

Access http://192.168.1.48:9091/transmission/web and login with transmission / transmission. Try to upload a torrent and make sure it gets downloaded into scratology's Downloads/incomplete folder.

Jackett

sudo apt-get install jq
jackettassets=$(curl --silent "https://api.github.com/repos/Jackett/Jackett/releases/latest" | grep -Po '"assets_url": "\K.*?(?=")')
jacketturl=$(curl --silent $jackettassets | jq -r '.[] | select(.name == "Jackett.Binaries.LinuxARM32.tar.gz") .browser_download_url')
wget ${jacketturl} -P /tmp/
sudo tar -xf /tmp/Jackett* -C /opt/
sudo chown -R pi:pi /opt/Jackett
sudo chmod +x /opt/Jackett/jackett
sudo /opt/Jackett/install_service_systemd.sh

Finally access the service using http://192.168.1.48:9117/.

Sonarr

wget "https://services.sonarr.tv/v1/download/main/latest?version=4&os=linux&arch=arm" -O /tmp/sonarr_v4.tar.gz
sudo tar -xvzf /tmp/sonarr_v4.tar.gz -C /opt/
sudo chown -R pi:pi /opt/Sonarr
sudo mkdir /var/lib/sonarr
sudo chown -R pi:pi /var/lib/sonarr

Then add this into this file : sudo nano /etc/systemd/system/sonarr.service

[Unit]
Description=Sonarr Daemon
After=network-online.target
RequiresMountsFor=/media/scratology/TVShows

[Service]
User=pi
Group=pi
Type=simple
ExecStart=/opt/Sonarr/Sonarr -nobrowser -data=/var/lib/sonarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure
StandardOutput=null

[Install]
WantedBy=multi-user.target

Then :

sudo systemctl enable sonarr
sudo service sonarr start

Finally, access http://192.168.1.48:8989/.

Radarr

radarrurl=$(curl -s https://api.github.com/repos/Radarr/Radarr/releases | grep linux-core-arm.tar.gz | grep browser_download_url | head -1 | cut -d \" -f 4)
wget ${radarrurl} -P /tmp/
sudo tar -xvzf /tmp/Radarr.master.*.linux-core-arm.tar.gz -C /opt/
sudo chown -R pi:pi /opt/Radarr

Then add this into this file : sudo nano /etc/systemd/system/radarr.service

[Unit]
Description=Radarr Daemon
After=network-online.target
RequiresMountsFor=/media/scratology/Movies

[Service]
User=pi
Group=pi
Type=simple
ExecStart=/opt/Radarr/Radarr -nobrowser
TimeoutStopSec=20
KillMode=process
Restart=on-failure
StandardOutput=null

[Install]
WantedBy=multi-user.target

Then :

sudo systemctl enable radarr
sudo service radarr start

Finally access http://192.168.1.48:8310/.

Configuration

This part is up to you. It's too specific for me to document.

@gnutix

gnutix commented Aug 24, 2026

Copy link
Copy Markdown
Author

Restoring the Pi media stack onto fresh media

Written 2026-08-24. For: raspbian (Raspberry Pi 4B, 192.168.1.48).

Honest expectation: 2–3 hours, not 10 minutes. The application data is a
genuine "restore a backup". The host underneath it — OS, static IP, NFS mounts,
Docker — has to be rebuilt first, and that is the bulk of the time.

This procedure has never been rehearsed. It is written from what was actually
installed, but the first real run will find something. Budget accordingly.


What you would and would not lose

Sonarr / Radarr library databases, quality profiles, history Safe (NAS + laptop)
Bazarr config, language profiles, provider logins Safe
Jackett indexer configs + API key Safe
Transmission settings Safe
docker-compose.yml, all three automation scripts + systemd units Safe
Host config: fstab, hosts, dhcpcd, sysctl, tmpfiles, journald Safe
Media files (they live on the NAS, never on the SD card) Untouched
Gmail app password (/etc/msmtprc) Deliberately not backed up — recreate
Active torrents mid-download Lost; re-add them
The OS itself, Docker install, apt packages Rebuilt from scratch

Nothing we built is lost. But it is not push-button.


Where the backups are

NAS:  /volume2/Downloads/rpi-backups/
        auto/config-YYYYMMDD-HHMM.tar.gz   <- daily, keeps 7, ~56 MB  ** USE THIS **
        20260824/apps.tar.gz               <- one-off full copy, 395 MB
        20260824/system.tar.gz
        20260824/radarr-5.28-pre-v6.tar.gz <- pre-v6 rollback, probably irrelevant now
Laptop: ~/Downloads/rpi-backups/20260824/

From a laptop with the NAS mounted, or ssh scratology (user <nas-user>, port <ssh-port>).

Check the date on the newest auto/ archive before trusting it. If the Pi
died a week before you noticed, the newest backup is from the day it died — that
is fine. If backups stopped much earlier, the daily timer had already failed, and
you should fall back to 20260824/.


Step 1 — Flash the OS

Raspberry Pi Imager → Raspberry Pi OS Lite (64-bit).

Take the 64-bit build. The old card ran 32-bit userland on a 64-bit kernel,
which is what forced seccomp=unconfined on every container and left glibc too
old for Jackett and Radarr. Going 64-bit removes all of that permanently. If you
do, delete every security_opt: seccomp=unconfined line and the
platform: pins from docker-compose.yml
— they are workarounds you no longer
need. Leave no-new-privileges and cap_drop alone.

FlareSolverr's platform: linux/arm/v7 can also go; use arm64 there too.

In Imager's settings, pre-set: hostname raspbian, user pi, enable SSH, add
your public key. Saves a monitor and keyboard.

Step 2 — Static IP and SSH

sudo nano /etc/dhcpcd.conf
interface eth0
static ip_address=192.168.1.48/17
static routers=192.168.1.1
static domain_name_servers=192.168.1.1

(Or just restore system/etc/dhcpcd.conf from the archive.) Reboot, confirm
ssh raspbian works from the laptop.

Step 3 — NFS mounts

sudo apt-get update && sudo apt-get install -y nfs-common
echo '192.168.1.42 scratology-wired' | sudo tee -a /etc/hosts
sudo mkdir -p /media/scratology/{Downloads,Movies,TVShows}

Restore system/etc/fstab's three NFS lines, then:

sudo mount -av
ls /media/scratology/Movies      # must list ~156 entries

Do not continue until this works. Every later step depends on it, and the
backups themselves live on this mount.

Step 4 — Docker

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker pi

Then log out and back in for the group to take effect.

If containerd.service is ever masked (ls -l /etc/systemd/system/containerd.service
showing a link to /dev/null), remove it. That single thing broke a previous
Docker install here and produces a very unhelpful error.

Step 5 — Restore the archive

cd /tmp
LATEST=$(ls -1t /media/scratology/Downloads/rpi-backups/auto/config-*.tar.gz | head -1)
echo "$LATEST"          # sanity-check the date
mkdir restore && tar xzf "$LATEST" -C restore && cd restore

# app configs
sudo mkdir -p /opt/docker
sudo cp -a sonarr radarr bazarr jackett /opt/docker/
sudo chown -R pi:pi /opt/docker

# host config, scripts and systemd units — paths are preserved in the archive
sudo cp -a system/. /
sudo systemctl daemon-reload

Restore Transmission separately if you still run it natively:

sudo apt-get install -y transmission-daemon
sudo systemctl stop transmission-daemon
sudo cp transmission/settings.json /etc/transmission-daemon/settings.json
sudo systemctl start transmission-daemon

Step 6 — Start the stack

cd /opt/docker && docker compose up -d
docker compose ps

Give it several minutes — Jackett alone takes ~20 s and Bazarr ~60 s on a Pi.

Then check each: Sonarr :8989, Radarr :8310, Jackett :9117,
Bazarr :6767, Transmission :9091, FlareSolverr :8191.

Radarr is on 8310, not the usual 7878.

Step 7 — Recreate the mail credentials

The only thing not in the backup. Get a Google app password
(https://myaccount.google.com/apppasswords — needs 2-Step Verification on), then:

sudo apt-get install -y msmtp msmtp-mta sqlite3 rsync
sudo tee /etc/msmtprc >/dev/null <<'EOF'
defaults
auth           on
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        /var/log/msmtp.log

account        gmail
host           smtp.gmail.com
port           587
from           <your-email@domain.com>
user           <your-email@domain.com>
password       PUT_APP_PASSWORD_HERE

account default : gmail
EOF
sudo chown root:root /etc/msmtprc && sudo chmod 600 /etc/msmtprc

sqlite3 and rsync are required by media-backup; msmtp will silently refuse
to run if the file is not mode 600.

Step 8 — Re-enable automation

sudo systemctl enable --now media-health.timer media-backup.timer media-update.timer
systemctl list-timers 'media-*' --no-pager
sudo systemctl start media-health.service      # should email you

Step 9 — Verify properly

Don't assume. Check:

# indexers reachable
SK=$(grep -oP '(?<=<ApiKey>)[^<]+' /opt/docker/sonarr/config.xml)
curl -s -m 500 -X POST localhost:8989/api/v3/indexer/testall -H "X-Api-Key: $SK"

# root folders visible (accessible must be true)
curl -s localhost:8989/api/v3/rootfolder -H "X-Api-Key: $SK"
  • Sonarr shows ~31 series, Radarr ~162 movies
  • Both root folders accessible: true
  • Bazarr lists your series and movies
  • One manual subtitle search returns results
  • A test grab actually downloads

Known gotchas on a rebuild

  • NFS automount + containers. fstab uses x-systemd.automount. Bind-mounting
    an autofs path into a container does not reliably trigger the mount. ls each
    media path before docker compose up. If containers see empty directories,
    drop x-systemd.automount from those three fstab lines.
  • Sonarr's image tag. The compose file pins :develop deliberately, because
    :latest was an older build (2979 vs 3001) and pushing a newer database into
    an older binary risks corrupting it. Do not "tidy" that to :latest.
  • FlareSolverr timeout. FlareSolverrMaxTimeout must be 140000 in Jackett's
    ServerConfig.json; the 55 s default is too short for Chrome on a Pi.
  • OpenSubtitles wants your OpenSubtitles USERNAME, not the email address.
  • If you stayed on 32-bit, every arm64 container still needs
    seccomp=unconfined or it dies with exit 159 (SIGSYS).

If the app data itself is damaged

Sonarr and Radarr keep their own native backup zips inside their config trees
(Backups/manual/), which are captured in the archive. Those are the officially
supported restore path — use them via each app's UI (System → Backup → Restore)
rather than hand-copying a database.


Reduce the pain before it happens

  1. Do a dry run. Restore onto a spare card and boot it once with the Pi
    offline. Two hours now, and it converts every unknown above into a known.
  2. Buy the card. A 64 GB SanDisk High Endurance or Samsung PRO Endurance is
    ~CHF 15–25, and it makes this a planned migration rather than an outage.
  3. Check the backups are still running. ls -lt /media/scratology/Downloads/rpi-backups/auto/
    — the newest file should be less than a day old. The daily health email
    covers most failures, but a silently broken backup timer is precisely the
    class of problem that hides.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment