Installing Transmission using Docker in LibreElec

Setting up Transmission on LibreElec

What you will need

  • Raspberry RP4 with 4GB was what I used (I run many containers)
  • Network locations on HDD / SSD for data (Avoid USB Flash drives and MicroSD) as these won't take the constant write cycles. HDD are fine USB units that work best using the RP4 USB3 ports and use POWERED drives when using large HD / SSD these also power off when not in use and spin up within a few seconds.
  • A fast boot USB device (make sure it supports UASP) the nVME units are fast and robust as are USB to SATA and SSDs for booting at cheap. But if you are on a budget USB HD can be used but watch the power use, be careful not to move HD when running as its a good way to crash the disk. I prefer large NTFS formatted HDD powered from mains for data (robust) with small boot SSDs for Librelec and Docker + transmission. This allows you to replace the boot device/software with no impact on the data HDD.
  • Spare External SSDs for backups. spare cables, PSUs, MicroSD and RPi4 for debugging

Method

  1. Update Librelec to the current version and ensure stable 24x7 operation
  2. Reboot, Uninstall Portainer and Docker if needed.
  3. Install a USB3 externally powered large HD
  4. Make a directory on the external drive with a transmission folder containing 2 subfolders 1) Watch, this is where you can drop torrents, I prefer to use the Chrome Torrent Adder add-on and an RSS feed from ShowRSS with automated Python scripts based on feed transmission to remove completed seeds 2) Downloads to hold a complete and incomplete directory see below bullet ten 
  5. First, start up a test server using the external HD in my case /var/media/10TB/_transmission
  6. To install and start a persistent server enter this on one line, be sure to check the directories exist ( a more complete example is given below but this gets a basic server up and running docker run -d --restart=always --name transmission -u $(id -u) -v /var/media/10TB/_transmission/watch:/to_download -v /var/media/10TB/_transmission/downloads:/output -p 9091:80 -p 51413:51413 -p 51413:51413/udp -e PORT=80 jaymoulin/transmission
  7. To add a username and password enter this on one line docker exec transmission configure <username> <password>
  8. To update docker exec transmission apk upgrade --update --no-cache transmission-daemon && docker restart transmission
  9. Code Hosted here: https://gitlab.com/jaymoulin/docker-transmission 
  10. Transmission can support separate folders for complete and incomplete files off the main output folder in my case off /var/media/10TB/_transmission/downloads 
  11. I created directories for the output directory called completed and incomplete
  12. Click the Cog on the Web UI to set up separate folders for complete and incomplete you can back up these settings to a json file to restore from a backup as needed. I'd recommend setting up options on the command line and setting up a volume for the transmission servers configuration, this is outlined in detail with a working example in the following sections.

Common Commands for Managing Docker

Seeing What's there

Docker ps lists all RUNNING contains use the -all flag to list both running and non-running containers

docker ps
docker ps -all
docker images

Cleaning Up Docker images while you test..

To stop the docker process, remove the docker container and then delete the docker image used to run that container these are the commands. You have to stop the container before you can remove the container. Only when no containers refer to a docker image can the image be removed.

This is useful to clean up when you are testing

docker stop  transmission
docker rm transmission
docker rmi jaymoulin/transmission


Once the Server is working as you want...

Take note of the command you used to get things working just the way you wanted. In my case, this single-line command would create a persistent transmission server that survives restarts.

To understand what is happening I'm calling the docker program with the run parameter to run a server in this case jaymoulin/transmission

The rest of the command line does 2 things

  1. sets up volumes for the docker image. Volumes map the docker image's virtual directories to the physical directories on my server, in this case, a Rasberry Pi 4 with 4 GB RAM running Libreelec with the docker add-on installed. You could of course be using a Debian, Armbian, or Ubuntu server the commands are the same. The first time you run the command it will search for existing local images and when they are not found docker will download them automatically.
  2. The rest of the command line sets up environment variables for the docker image these are command options for transmission. In my case I want partial files kept in an incomplete directory with a .PART extension and only when completed do I want them moved to the completed directory. I also switch off client authentication and cap the upload speed as I have limited upload on my ADSL line. I also make the config directory real by linking it to a physical directory. This means that any settings I save, say in the Web GUI are written to the local disk and so when the server restarts docker will use the saved settings. If I did not do this then the default settings from the docker image would be used. Remember to stop the docker transmission process if you plan to manually edit the settings.json file directly, then restart the process and it will read in your changes.

docker run -d --restart=always --name transmission -u $(id -u) -v /var/media/10TB/_transmission/watch:/to_download -v /var/media/10TB/_transmission/downloads/complete:/output -v /var/media/10TB/_transmission/downloads/config:/config -v /var/media/10TB/_transmission/downloads/incomplete:/incomplete -p 9091:80 -p 51413:51413 -p 51413:51413/udp -e PORT=80 -e "TRANSMISSION_INCOMPLETE_DIR_ENABLED=true" -e "TRANSMISSION_INCOMPLETE_DIR=/incomplete" -e "TRANSMISSION_BLOCKLIST_ENABLED=true" -e "TRANSMISSION_BLOCKLIST_URL=http://john.bitsurge.net/public/biglist.p2p.gz" -e "TRANSMISSION_RPC_AUTHENTICATION_REQUIRED=false" -e "TRANSMISSION_SPEED_LIMIT_UP=100" -e "TRANSMISSION_SPEED_LIMIT_UP_ENABLED=true" -e "TRANSMISSION_DOWNLOAD_DIR=/output" jaymoulin/transmission


Removing completed jobs automatically

I use a cronjob to periodically check my servers and remove completed jobs. To remove completed torrents you can use the transmission-remote tool this is a script from https://gist.github.com/pawelszydlo/e2e1fc424f2c9d306f3a 
I've hard-coded a test server with no username and password as an example. Once you create the shell script set the execute bit and add a corn job.

#!/bin/bash
# Clears finished downloads from Transmission.
# Version: 1.1
#
# Newest version can always be found at:
# https://gist.github.com/pawelszydlo/e2e1fc424f2c9d306f3a
#
# Server string is resolved in this order:
# 1. TRANSMISSION_SERVER environment variable
# 2. Parameters passed to this script
# 3. Hardcoded string in this script (see below).

# Server string: "host:port --auth username:password"
SERVER="192.168.0.162:9091 --auth : "

# Which torrent states should be removed at 100% progress.
DONE_STATES=("Seeding" "Stopped" "Finished" "Idle")

# Get the final server string to use.
if [[ -n "$TRANSMISSION_SERVER" ]]; then
    echo -n "Using server string from the environment: "
    SERVER="$TRANSMISSION_SERVER"
elif [[ "$#" -gt 0 ]]; then
    echo -n "Using server string passed through parameters: "
    SERVER="$*"
else
    echo -n "Using hardcoded server string: "
fi
echo "${SERVER: : 10}(...)"  # Truncate to not print auth.

# Use transmission-remote to get the torrent list from transmission-remote.
TORRENT_LIST=$(transmission-remote $SERVER --list | sed -e '1d' -e '$d' | awk '{print $1}' | sed -e 's/[^0-9]*//g')

# Iterate through the torrents.
for TORRENT_ID in $TORRENT_LIST
do
    INFO=$(transmission-remote $SERVER --torrent "$TORRENT_ID" --info)
    echo -e "Processing #$TORRENT_ID: \"$(echo "$INFO" | sed -n 's/.*Name: \(.*\)/\1/p')\"..."
    # To see the full torrent info, uncomment the following line.
    # echo "$INFO"
    PROGRESS=$(echo "$INFO" | sed -n 's/.*Percent Done: \(.*\)%.*/\1/p')
    STATE=$(echo "$INFO" | sed -n 's/.*State: \(.*\)/\1/p')

    # If the torrent is 100% done and the state is one of the done states.
    if [[ "$PROGRESS" == "100" ]] && [[ "${DONE_STATES[@]}" =~ "$STATE" ]]; then
        echo "Torrent #$TORRENT_ID is done. Removing torrent from list."
        transmission-remote $SERVER --torrent "$TORRENT_ID" --remove
    else
        echo "Torrent #$TORRENT_ID is $PROGRESS% done with state \"$STATE\". Ignoring."
    fi
done

Roadblocks that cost me days!

I thought it was me but on reflection, it should not be this hard to set up an RP4. It's possible that my monitors just did not work well with the RPi4.
  • I've years of experience with RP2 and RP3 using LibreElec with TVheadend and NAS-attached media. I've also set up a 4k streaming LibreElec-based instance running on RP4 for 4k media. You'd think setting up an RPi4 for Transmission would be easy, it was not!
  • I'd already set up the RPi4 with Librelec and Transmission before and had no issues. Then the setup stopped working I took a look and the Docker add-on needed an update from within LE and it was working again. A few weeks later the system stopped and I could get no video. The only change had been a firmware update to the RPi4 (EEPROM) to Jan 23.
  • My first thought was the current supply for the RP4i. The new official PSU still would not provide an image reliably. Replace the RPi4 still no luck. Replaced the micro-HDMI to HDMI cable, no luck. I was just looking for the boot logo (no OS) at this point. In desperation, I tried a Micro-HDMI to Full-size adaptor and a full-size HDMI to DVI cable and every combination worked with video output!
  • I had issues with the RPi4 support for HDMI using many different cables and the official PSU. It really depends on your display device. I found an HDMI to micro HDMI adaptor and a Full-size HDMI to DVI cable that worked. Direct micro HDMI to full-size HDMI (3 makes and models of cable no reliable video mostly blank screen) did not work and micro-HDMI to full-size HDMI to DVI adapter (black screen). I've been using RPi4 since they came out and it was never as bad as with the latest Jan 23 firmware in the EEPROM.
  • I had issued my USB-SATA convertor but realised this was the UASP issue. I used an NVME SSD in a USB enclosure with UASP support and things were fine. I also had success with MicroSD but the boot time and performance of the 1TB nVME is fast and reliable for boot and operation. I used the Raspberry Pi Imager to put the LibreElec image on the USB-attached nVME drive.
  • Post Mortem I think I had more than one issue that made debugging hard. Once the video was working, I was able to diagnose the storage issues due to the lack of UASP support.
  • Regardless for a teaching platform that is supposedly mature the state of  RPi4 and LE can be tough even for experienced users!
  • Finally, I used Portainer to set up transmission but the setup was lost in the crash, my LE backups would not restore reliably (USAP?) and once that was fixed they'd create non-working transmission instances when restored. I'm new to Portainer and have only used it once I found it hard work. Things like creating volumes were just silly and complex. So for the reinstall after having issues with a lack of transmission images for RPi4. I just ditched Portainer and used a CLI installation. The only customisation is the directories to be used. The rest of the step is standard
  • Software Headaches. Previously I'd used the Linux Server repository for Portainer and Transmission but think the route no longer works. TBH the hassle of using Portainer never made much sense so I used a command line install/update method