BitTorrent Server
Running Transmission as a server daemon rather than a desktop application changes how BitTorrent works in practice. Downloads happen on the server regardless of whether the desktop is on. Seeding continues indefinitely. Completed files are immediately available on the NAS to every device on the network. The web interface and transmission-remote allow managing downloads from any device without installing a client.
The source material covers Transmission on Ubuntu 14.04 with Upstart init scripts and a separate FlexGet daemon for automated downloads. This page updates the configuration for Ubuntu 24.04 and systemd, with FlexGet as an optional addition.
Container setup
Clone the base template:
pct clone 100 145 --hostname bittorrent --full
pct start 145
Inside the container:
hostnamectl set-hostname bittorrent.yourdomain.net
sed -i 's/base-template/bittorrent/g' /etc/hosts
Installation
Transmission is in the Ubuntu repositories:
sudo apt install transmission-daemon
This creates:
- A
debian-transmissionuser and group /var/lib/transmission-daemon/for state data and configuration/etc/transmission-daemon/settings.jsonas the configuration file- A systemd service
transmission-daemonwhich starts automatically
Stop the daemon before configuring it. Transmission overwrites settings.json with its in-memory state when it shuts down, so any changes made while it is running are lost:
sudo systemctl stop transmission-daemon
Download directories
Create the download directories on NFS-mounted NAS storage:
sudo mkdir -p /var/lib/transmission-daemon/downloads/complete
sudo mkdir -p /var/lib/transmission-daemon/downloads/incomplete
sudo chown -R debian-transmission:debian-transmission \
/var/lib/transmission-daemon/downloads
If storing downloads on the NAS rather than locally, mount the NAS share first (covered in the NAS section) and create the directories on the mount point:
nas.yourdomain.net:/volume1/downloads /var/lib/transmission-daemon/downloads nfs defaults,_netdev,nofail 0 0
Configuration
Edit /etc/transmission-daemon/settings.json. The daemon must be stopped first:
{
"alt-speed-down": 500,
"alt-speed-enabled": false,
"alt-speed-time-begin": 540,
"alt-speed-time-day": 127,
"alt-speed-time-enabled": false,
"alt-speed-time-end": 1020,
"alt-speed-up": 500,
"bind-address-ipv4": "0.0.0.0",
"bind-address-ipv6": "::",
"blocklist-enabled": true,
"blocklist-url": "https://github.com/nicehash/NiceHashQuickMiner/raw/master/nicehash_badpeers.txt",
"cache-size-mb": 256,
"dht-enabled": true,
"download-dir": "/var/lib/transmission-daemon/downloads/complete",
"download-queue-enabled": true,
"download-queue-size": 5,
"encryption": 1,
"idle-seeding-limit": 30,
"idle-seeding-limit-enabled": false,
"incomplete-dir": "/var/lib/transmission-daemon/downloads/incomplete",
"incomplete-dir-enabled": true,
"lpd-enabled": false,
"message-level": 2,
"peer-congestion-algorithm": "",
"peer-id-ttl-hours": 6,
"peer-limit-global": 200,
"peer-limit-per-torrent": 50,
"peer-port": 51413,
"peer-port-random-high": 65535,
"peer-port-random-low": 49152,
"peer-port-random-on-start": false,
"peer-socket-tos": "lowcost",
"pex-enabled": true,
"port-forwarding-enabled": false,
"prealloc-min-overhead-gb": 1,
"prefetch-enabled": true,
"queue-stalled-enabled": true,
"queue-stalled-minutes": 30,
"ratio-limit": 2,
"ratio-limit-enabled": true,
"rename-partial-files": true,
"rpc-authentication-required": true,
"rpc-bind-address": "0.0.0.0",
"rpc-enabled": true,
"rpc-host-whitelist": "bittorrent.yourdomain.net,10.1.0.*",
"rpc-host-whitelist-enabled": true,
"rpc-password": "your-password-here",
"rpc-port": 9091,
"rpc-url": "/transmission/",
"rpc-username": "transmission",
"rpc-whitelist": "127.0.0.1,::1,10.*.*.*",
"rpc-whitelist-enabled": true,
"scrape-paused-torrents-enabled": true,
"script-torrent-added-enabled": false,
"script-torrent-done-enabled": false,
"seed-queue-enabled": false,
"speed-limit-down": 0,
"speed-limit-down-enabled": false,
"speed-limit-up": 0,
"speed-limit-up-enabled": false,
"start-added-torrents": true,
"trash-original-torrent-files": false,
"umask": 2,
"utp-enabled": true,
"watch-dir": "/var/lib/transmission-daemon/watch",
"watch-dir-enabled": true
}
Key settings to review:
rpc-password: set a strong password. Transmission hashes it on next startup. Generate one in KeePassXC and store it there.
rpc-whitelist: restricts which IPs can access the web interface. The 10.*.*.* pattern allows access from all three sites via the WireGuard VPN.
rpc-host-whitelist: add the container hostname and any IP addresses used to access the web interface. Missing entries cause 421 Misdirected Request errors.
ratio-limit: set to 2.0 by default, meaning Transmission stops seeding when it has uploaded twice what it downloaded. Adjust to taste or disable entirely.
umask: set to 2 so downloaded files are group-writable, allowing other services (Jellyfin, Kavita) to read them without permission issues.
watch-dir: Transmission watches this directory for new .torrent files and starts them automatically. Create the directory:
sudo mkdir -p /var/lib/transmission-daemon/watch
sudo chown debian-transmission:debian-transmission \
/var/lib/transmission-daemon/watch
Start and verify
sudo systemctl start transmission-daemon
sudo systemctl enable transmission-daemon
sudo systemctl status transmission-daemon
Verify the RPC port is listening:
ss -tlnp | grep 9091
Test the RPC connection locally:
transmission-remote -n 'transmission:your-password-here' --session-info
nginx reverse proxy
Add a virtual server or location block to the nginx web container to expose the Transmission web interface:
# Add to an existing server block or create a dedicated one
location /transmission/ {
proxy_pass http://10.1.0.x:9091/transmission/;
include snippets/proxy-headers.conf;
# Restrict to internal network only
include snippets/internal-only.conf;
}
Or as a standalone virtual server:
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name bittorrent.yourdomain.net;
ssl_certificate /etc/ssl/certs/bittorrent.yourdomain.net.crt;
ssl_certificate_key /etc/ssl/private/bittorrent.yourdomain.net.key;
# Internal access only
include snippets/internal-only.conf;
include snippets/security-headers.conf;
location /transmission/ {
proxy_pass http://10.1.0.x:9091/transmission/;
include snippets/proxy-headers.conf;
}
access_log /var/log/nginx/bittorrent.access.log main;
error_log /var/log/nginx/bittorrent.error.log;
}
Firewall rules
Open the BitTorrent peer port on the UDM-SE for inbound peer connections. Without this, Transmission operates in passive mode and some torrents connect slowly or not at all.
On the UDM-SE, add a port forward:
| Setting | Value |
|---|---|
| Port | 51413 |
| Protocol | TCP + UDP |
| Forward IP | Container IP address |
| Forward Port | 51413 |
transmission-remote usage
Useful transmission-remote commands from the desktop or server:
# Alias for convenience (add to ~/.bashrc)
alias t='transmission-remote -n "transmission:your-password" -l 10.1.0.x'
# List all torrents
transmission-remote 10.1.0.x -n 'transmission:password' -l
# Add a torrent by URL or file
transmission-remote 10.1.0.x -n 'transmission:password' -a https://example.com/file.torrent
# Add a magnet link
transmission-remote 10.1.0.x -n 'transmission:password' -a 'magnet:?xt=urn:...'
# Check session stats
transmission-remote 10.1.0.x -n 'transmission:password' -st
# Remove a torrent (keep data)
transmission-remote 10.1.0.x -n 'transmission:password' -t 1 --remove
# Remove a torrent and delete data
transmission-remote 10.1.0.x -n 'transmission:password' -t 1 --remove-and-delete
FlexGet: automated downloads (optional)
FlexGet is a Python-based automation tool that watches RSS feeds and adds matching torrents to Transmission automatically. It is useful for automatically downloading new episodes of TV shows, new releases from followed content creators, or any torrent feed with predictable naming.
Install FlexGet in a virtual environment owned by the debian-transmission user:
sudo -u debian-transmission python3 -m venv \
/var/lib/transmission-daemon/flexget-venv
sudo -u debian-transmission \
/var/lib/transmission-daemon/flexget-venv/bin/pip install flexget
Create the FlexGet configuration directory:
sudo mkdir -p /var/lib/transmission-daemon/flexget
sudo chown debian-transmission:debian-transmission \
/var/lib/transmission-daemon/flexget
Create /var/lib/transmission-daemon/flexget/config.yml:
# FlexGet configuration
# /var/lib/transmission-daemon/flexget/config.yml
tasks:
# Example: download a podcast feed
example-podcast:
rss: https://example.com/podcast/rss
accept_all: yes
transmission:
host: localhost
port: 9091
username: transmission
password: your-password-here
path: /var/lib/transmission-daemon/downloads/complete/podcasts/
Create a systemd service for the FlexGet daemon:
sudo tee /etc/systemd/system/flexget.service << 'EOF'
[Unit]
Description=FlexGet daemon
After=transmission-daemon.service
Requires=transmission-daemon.service
[Service]
Type=simple
User=debian-transmission
Group=debian-transmission
WorkingDirectory=/var/lib/transmission-daemon/flexget
ExecStart=/var/lib/transmission-daemon/flexget-venv/bin/flexget \
--loglevel warning daemon start
ExecStop=/var/lib/transmission-daemon/flexget-venv/bin/flexget \
daemon stop
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now flexget
The source material uses an Upstart init script for FlexGet. The systemd service above replaces it. The After=transmission-daemon.service and Requires=transmission-daemon.service directives ensure FlexGet only starts after Transmission is running, which is the equivalent of the original start on started transmission-daemon Upstart directive.
Connecting the desktop Transmission client
The Transmission desktop client configured in the desktop Toolbox section connects to the server daemon rather than managing downloads locally. In Transmission on the desktop, go to Edit > Preferences > Remote and configure:
| Setting | Value |
|---|---|
| Enable remote access | Yes |
| Host | bittorrent.yourdomain.net or 10.1.0.x |
| Port | 9091 |
| Authentication | Yes |
| Username | transmission |
| Password | The password from settings.json |
All torrent management from the desktop GUI now happens on the server daemon. Downloads run on the server whether or not the desktop is on.
Stop the Transmission daemon before editing
settings.json. Transmission overwrites the configuration file with its in-memory state when it exits. Changes made to the file while the daemon is running are silently discarded when it next shuts down. This is the most common source of confusion when configuring Transmission for the first time.
Roll Your Own Network / Server. Next: Calibre.