Transmission Kubernetes Setup on Ubuntu

Install the k8s cluster if not alreadly installed

1
2
microk8s enable dns storage helm3
microk8s status

Pull the helm chart

1
2
microk8s helm repo add truecharts https://charts.truecharts.org/
microk8s helm pull truecharts/transmission

Modify the values.yaml file to set the configuration

1
2
3
tar -xvf ./transmission-*.tgz
cd transmission
cat values.yamls

Make the data directory

CONTAINER_ROOT_PATH

1
2
3
mkdir -p CONTAINER_ROOT_PATH
sudo chown -R 777 CONTAINER_ROOT_PATH

Create Persistent Volumes and Persistent Volume Claims

Transmission can think you deleted all your files if the harddisk containing your files fails to mount and the configuration files are on a different disk.

Persistent Volumes

Config

transmission-config-pv.yaml

HOSTNAME: Local computer hostname

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: PersistentVolume
metadata:
name: transmission-config-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: CONTAINER_ROOT_PATH/config # This must exist on the host
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- HOSTNAME

microk8s kubectl apply -f transmission-config-pv

1
2
3
microk8s kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
transmission-config-pv 100Gi RWO Retain Bound default/transmission-config-pvc local-storage 9m44s

Data

transmission-data-pv.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: PersistentVolume
metadata:
name: transmission-data-pv
spec:
capacity:
storage: 5Ti
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: CONTAINER_ROOT_PATH/data # This must exist on the host
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- HOSTNAME

microk8s kubectl apply -f transmission-data-pv.yaml

microk8s kubectl get pv

1
2
3
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                 STORAGECLASS        REASON   AGE
transmission-config-pv 100Gi RWO Retain Bound default/transmission-config-pvc local-storage 3h28m
transmission-data-pv 5Ti RWO Retain Bound default/transmission-data-pvc local-storage 19s

Persistent Volume Claims

Config

transmission-config-pvc.yaml
Bonds to transmission-config-pv

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: transmission-config-pvc
spec:
storageClassName: local-storage # Empty string must be explicitly set otherwise default StorageClass will be set
accessModes:
- ReadWriteOnce
volumeName: transmission-config-pv
resources:
requests:
storage: 100Gi

microk8s kubectl apply -f transmission-config-pvc.yaml

Data

transmission-data-pvc.yaml
Bonds to transmission-data-pvc

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: transmission-data-pvc
spec:
storageClassName: local-storage # Empty string must be explicitly set otherwise default StorageClass will be set
accessModes:
- ReadWriteOnce
volumeName: transmission-data-pv
resources:
requests:
storage: 5Ti

microk8s kubectl apply -f transmission-data-pvc.yaml

microk8s kubectl get pvc

1
2
3
NAME                          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS        AGE
transmission-config-pvc Bound transmission-config-pv 100Gi RWO local-storage 3h21m
transmission-data-pvc Bound transmission-data-pv 5Ti RWO local-storage 51s

Pull the syncthing helm chart

1
2
3
microk8s helm repo add truecharts https://charts.truecharts.org/
microk8s helm repo update
microk8s helm pull truecharts/transmission

Modify the values.yaml file to set the configuration

We’re going to set the transmission configuration within the container.

1
2
3
tar -xvf ./transmission-*.tgz
cd transmission
nano values.yamls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
image:
repository: tccr.io/truecharts/transmission
pullPolicy: IfNotPresent
tag: v4.0.3@sha256:245158e56dae5ca2da2cac5e9e85d4879685e8f302ed955ba144162d504307e4

service:
main:
ports:
main:
port: WEBGUI_PORT_NUMBER
torrent:
enabled: true
ports:
torrent:
enabled: true
port: TRANSMISSION_PORT
protocol: tcp
torrentudp:
enabled: true
port: TRANSMISSION_PORT
protocol: udp

workload:
main:
podSpec:
containers:
main:
probes:
liveness:
enabled: true
type: tcp
startup:
enabled: true
type: tcp
readiness:
enabled: true
type: tcp
env:
# PUID: 1001
# URL is set here so it wont be able to get overwritten by the user
# as this will break the probes, if the need arises we can expose it.
TRANSMISSION__RPC_URL: "/transmission"
TRANSMISSION__RPC_USERNAME: "USERNAME"
TRANSMISSION__RPC_PASSWORD: "RANDOM_PASSWORD"
TRANSMISSION__RPC_AUTHENTICATION_REQUIRED: true
# TRANSMISSION__ALT_SPEED_DOWN: 50
# TRANSMISSION__ALT_SPEED_ENABLED: false
# TRANSMISSION__ALT_SPEED_TIME_BEGIN: 540
# TRANSMISSION__ALT_SPEED_TIME_DAY: 127
# TRANSMISSION__ALT_SPEED_TIME_ENABLED: false
# TRANSMISSION__ALT_SPEED_TIME_END: 1020
# TRANSMISSION__ALT_SPEED_UP: 50
# TRANSMISSION__BIND_ADDRESS_IPV4: "0.0.0.0"
# TRANSMISSION__BIND_ADDRESS_IPV6: "::"
# TRANSMISSION__BLOCKLIST_ENABLED: true
# TRANSMISSION__BLOCKLIST_URL: "https://github.com/Naunter/BT_BlockLists/releases/download/v.1/bt_blocklists.gz"
TRANSMISSION__CACHE_SIZE_MB: 4
# TRANSMISSION__DHT_ENABLED: true
TRANSMISSION__DOWNLOAD_DIR: "/data/Completed"
# TRANSMISSION__DOWNLOAD_QUEUE_ENABLED: true
# TRANSMISSION__DOWNLOAD_QUEUE_SIZE: 5
# TRANSMISSION__ENCRYPTION: 1
# TRANSMISSION__IDLE_SEEDING_LIMIT: 30
# TRANSMISSION__IDLE_SEEDING_LIMIT_ENABLED: false
TRANSMISSION__INCOMPLETE_DIR: "/data/Incomplete"
# TRANSMISSION__INCOMPLETE_DIR_ENABLED: true
# TRANSMISSION__LPD_ENABLED: false
# TRANSMISSION__MESSAGE_LEVEL: 2
# TRANSMISSION__PEER_CONGESTION_ALGORITHM: ""
# TRANSMISSION__PEER_ID_TTL_HOURS: 6
# TRANSMISSION__PEER_LIMIT_GLOBAL: 200
# TRANSMISSION__PEER_LIMIT_PER_TORRENT: 50
TRANSMISSION__PEER_PORT: "{{ .Values.service.torrent.ports.torrent.port }}"
# TRANSMISSION__PEER_PORT_RANDOM_HIGH: 65535
# TRANSMISSION__PEER_PORT_RANDOM_LOW: 49152
# TRANSMISSION__PEER_PORT_RANDOM_ON_START: false
# TRANSMISSION__PEER_SOCKET_TOS: default"
# TRANSMISSION__PEX_ENABLED: true
# TRANSMISSION__PORT_FORWARDING_ENABLED: false
# TRANSMISSION__PREALLOCATION: 1
# TRANSMISSION__PREFETCH_ENABLED: true
# TRANSMISSION__QUEUE_STALLED_ENABLED: true
# TRANSMISSION__QUEUE_STALLED_MINUTES: 30
# TRANSMISSION__RATIO_LIMIT: 2
# TRANSMISSION__RATIO_LIMIT_ENABLED: false
# TRANSMISSION__RENAME_PARTIAL_FILES: true
# TRANSMISSION__RPC_BIND_ADDRESS: "0.0.0.0"
# TRANSMISSION__RPC_ENABLED: true
# TRANSMISSION__RPC_HOST_WHITELIST: ""
# TRANSMISSION__RPC_HOST_WHITELIST_ENABLED: false
TRANSMISSION__RPC_PORT: "{{ .Values.service.main.ports.main.port }}"
# TRANSMISSION__RPC_WHITELIST: ""
# TRANSMISSION__RPC_WHITELIST_ENABLED: false
# TRANSMISSION__SCRAPE_PAUSED_TORRENTS_ENABLED: true
# TRANSMISSION__SCRIPT_TORRENT_DONE_ENABLED: false
# TRANSMISSION__SCRIPT_TORRENT_DONE_FILENAME: ""
# TRANSMISSION__SEED_QUEUE_ENABLED: false
# TRANSMISSION__SEED_QUEUE_SIZE: 10
# TRANSMISSION__SPEED_LIMIT_DOWN: 100
# TRANSMISSION__SPEED_LIMIT_DOWN_ENABLED: false
# TRANSMISSION__SPEED_LIMIT_UP: 100
# TRANSMISSION__SPEED_LIMIT_UP_ENABLED: false
# TRANSMISSION__START_ADDED_TORRENTS: true
# TRANSMISSION__TRASH_ORIGINAL_TORRENT_FILES: false
# TRANSMISSION__UMASK: 2
# TRANSMISSION__UPLOAD_SLOTS_PER_TORRENT: 14
# TRANSMISSION__UTP_ENABLED: true
# TRANSMISSION__WATCH_DIR: "/watch"
# TRANSMISSION__WATCH_DIR_ENABLED: false

persistence:
config:
enabled: true
mountPath: "/config"
type: pvc
existingClaim: transmission-config-pvc
data:
enabled: true
mountPath: "/data"
type: pvc
existingClaim: transmission-data-pvc

portal:
open:
enabled: true

manifestManager:
enabled: false

Install the helm chart with our values.yaml file

1
microk8s helm install syncthing-server truecharts/syncthing --values ./values.yaml 

Check the status of the installed application.

1
2
3
4
5
microk8s status
microk8s kubectl describe pods
microk8s kubectl
microk8s kubectl logs
microk8s kubectl describe pods

Common Errors

Error from server (BadRequest): container in pod is waiting to start: ContainerCreating
You probably need to change the permissions on the PV directory. This path is what is written in the PersistentVolume in the path variable. A quick chmod -R 777 to this path will most likely fix the issue. The conatiner should update the permissions once it runs.

Service yaml file

We need to expose the service to the outside world.
Thankfully microk8s has a built in loadbalancer called metallb

WEBGUI_PORT_NUMBER: The port you wish to run the webgui on
TRANSMISSION_PORT: The port Transmission uses to comminicate with external parties.

transmission-service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: Service
metadata:
name: transmission-service
spec:
type: LoadBalancer
selector:
app.kubernetes.io/name: transmission
ports:
- name: transmission-tcp
protocol: TCP
port: TRANSMISSION_PORT
targetPort: TRANSMISSION_PORT
- name: transmission-udp
protocol: UDP
port: TRANSMISSION_PORT
targetPort: TRANSMISSION_PORT
- name: transmission-rpc-tcp
protocol: TCP
port: WEBGUI_PORT_NUMBER # web gui port
targetPort: WEBGUI_PORT_NUMBER
externalIPs:
- A.B.C.D

More information here
https://medium.com/swlh/kubernetes-external-ip-service-type-5e5e9ad62fcd

Apply the service

1
microk8s kubectl apply -f ./transmission-service.yaml 

Confirm the service is active

1
microk8s kubectl get services transmission-service
1
2
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                                           AGE
transmission-service LoadBalancer A.B.C.D X.Y.Z.A TRANSMISSION_PORT:TRANSMISSION_PORT_2/TCP,TRANSMISSION_PORT:TRANSMISSION_PORT_2 /UDP,WEBGUI_PORT_NUMBER:31056/TCP 9h

Firewall Rules

This assumes you are using ufw.
ufw is bascally a wrapper for IPTABLES. If you have ever used IPTABLES before you understand why ufw exists.
https://docs.syncthing.net/users/firewall.html

1
2
3
4
5
6
7
8

sudo ufw default allow routed

sudo ufw allow from A.B.C.0/24 to any port WEBGUI_PORT_NUMBER proto tcp
sudo ufw allow from E.F.G.H/24 to any port TRANSMISSION_PORT proto tcp
sudo ufw allow from E.F.G.H/24 to any port TRANSMISSION_PORT proto udp

sudo ufw status

References

https://kubernetes.io/docs/concepts/services-networking/service/