Preface

I’ve wanted to setup transmission on my server for a while. I just wanted to make sure no one could see the linux iso’s I was downloading. I’ve tried before but for some reason always struggled. I mean I had it working a few years ago but that was without docker. Now I have a little bit of a diffrent approch. I want to talk about that today.

Introduction

Torrenting over a VPN is fairly simple, it is the seeding that is the problem. Lots of VPN services either block outright the ability to see or open a random port that you have to figureout and add to your client. The worst part, it changes when you restart the VPN. So… how do I go about fixing that? I will be using the LSIO container for transmission. LSIO has the amazing ability to run “Private Custom Services”. This allows you to run a script along side what ever service is starting. So lets start some of the configs and scripts.

  1. Create the Docker File. Create a directory called transsmision and then create a file in there called DockerFile.
FROM linuxserver/transmission:latest

RUN apk add libnatpmp --no-cache

RUN mkdir /custom-cont-init.d

COPY ./open_port.sh /custom-services.d/
RUN chmod +x /custom-services.d/open_port.sh
  1. The script. In that same directory you will want to add a script called open_port.sh.
#!/bin/bash

# Wait for Transmission to start
while ! nc -z localhost 9091; do
  echo "Waiting for Transmission to start..."
  sleep 1
done

# Function to extract port number from natpmpc output
extract_port() {
    local output="$1"
    echo "$output" | sed -n 's/.*Mapped public port //p' | sed -n 's/\ .*//p'
}

# Run natpmpc commands and extract port numbers
tcp_output=$(natpmpc -a 1 0 tcp 60 -g 10.2.0.1)

# Extract and print port numbers
tcp_port=$(extract_port "$tcp_output")

# Update the Transmission peer listening port
transmission-remote -n "username:password" -p $tcp_port

# Check if the update was successful
if [ $? -eq 0 ]; then
    echo "Transmission peer listening port updated to $tcp_port"
else
    echo "Failed to update Transmission peer listening port"
fi
sleep 60m

In this script, you will need to specify the username and password, which isn’t great but I have set it to something generic as this is just my torrenting server. Maybe not the best idea, if you have any suggestions I am all ears (comments at bottom of page).

The first section of the script is checking to see if Transmission has started. Then it runs natpmpc to see what port is open. The script then calls extract_port() to filter out all the crap and just get the port number. Lastly, at the bottom, it uses transmission-remote to set the port. It will then sleep for sixty minits and then run again. This is just incase it changes. Also, if you don’t make it sleep, it will run constatnly which is probably not best practice. You could also set it to sleep indefinate, if you don’t want the script running evey hour.

  1. Wireguard Image We are going to need to get our Wireguard config from our VPN provider. For seeding to work you will obviously need a provider that support it, for me that is ProtonVPN. I’m not going to walk you through that section.

Right, now create a directory called wireguard and copy the wg0.conf file into there. Now we wan’t to create another DockerFile.

FROM linuxserver/wireguard:latest
COPY ./wg0.conf /config/wg_confs/wg0.conf

We will also want to edit the wg0.conf file to include these two lines:

[Interface]
...
PostUp = ip route add 192.168.1.0/24 via 172.20.0.1;
PreDown = ip route delete 192.168.1.0/24;

[Peer]
...

You will need to ensure that the subnets match your network setup.

  1. Now we move onto the docker-compose.yml file. Create this in the root of your docker folder, e.g. not in either the transmission or wireguard folders. This is the final bit before testing.
services:
  wireguard:
    container_name: wireguard
    build:
      context: wireguard/
      dockerfile: Dockerfile
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv4.conf.all.src_valid_mark=1
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Stockholm
      - VIRTUAL_HOST=127.0.0.1
      - VIRTUAL_PORT=9091
    ports:
      - 9091:9091
    volumes:
      - /lib/modules:/lib/modules
    restart: always

  transmission:
    container_name: transmission
    build:
      context: transmission/
      dockerfile: Dockerfile
    network_mode: "service:wireguard"
    depends_on:
      - wireguard
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Stockholm
    env_file:
      - ./.env
    volumes:
      - /location/too/transmission/transmission-config:/config
      - /location/too/transmission/downloads:/downloads
      - /location/too/transmission/watch:/watch
    restart: always

We will also want to create the .env file with username and password we want:

# TRANSMISSION
USER=username
PASS=password
  1. Running it. We should be able to get this working now. Run docker compose up -d

To test that trafic is going through the vpn, we can run:

docker exec transmission sh -c 'curl -Ss https://ipinfo.io'
docker exec wireguard sh -c 'curl -Ss https://ipinfo.io'

You should see: The IP should be diffrent from your home one.

If so, you can now check by going to the ip of your docker server with the port 9091. Go into the handburger menu at the top of the website and Edit Preferances -> Network. You should see Peer listening port as open.

If that is showing up, you should be all good. Feel free to ask any questions down bellow.