I've been using Transmission (OS X) for as long as I can remember. However, after the last MacBook Pro "update" (if you can call it that), I've switched all of my computers to using linux (personal and work computer, home media server, etc.). This switched gave me the opportunity to reevaluate how I handled some of my workflows and seek out different software options. I knew I wanted to go with a headless option for my new home server and so I decided to look at my torrent client options.

Before I break down my findings, it's worth mentioned my current procedure. I run several different instances of each client depending on its use, what its contents are, etc. My torrent client workflow is powered by a script running in cron that does the following:

  • If my VPN is disconnected and a client is running, it exits the client gracefully and quits.
  • If my VPN is active, it checks the VPN's interface IP address against the bound IP address for my torrent client. If the 2 differ, it exits the torrent client and quits.
  • If my VPN interface is active and my client isn't running, it updates the bound IP address for the client and starts up its daemon.

So here are the following client's I tried along with my findings. Please note that my findings are all based on an out-of-the-box solution. You can make any of these client applications better via plugins.


You can't mention a headless torrent client without the first response being rtorrent. rtorrent is a client that runs as a command line process with a nice TUI. It's fast, you can navigate into a torrent via the keyboard to view its detailed information, and it's extremely easy to set up and run. It's config file is easy to read and the configuration options weren't really lacking much of anything. So far I was happy, so I started to build a piece of automation to basically accomplish my workflow above.

Multi-instance Daemons

I won't lie, the biggest turn-off for me and rtorrent is that it's not a daemon. I can't start it up in a terminal session and let it go. rtorrent has to run in the background via either screen or tmux. So in order to achieve a daemon-like functionality with rtorrent (required to work with my automation), I had to create a service file. Not a huge deal, but still, not what I'd prefer.

So as long as the each service file contained its own instance-unique session file, the multi-daemon instance management wasn't too bad. So the command to launch an instance was as follows:


A problem I ran into, and I'll be honest I didn't realize this was an issue until after I moved on from rtorrent, was how to determine if the process was currently running. You'll notice in the following clients, I make use of their PID files, and I assume rtorrent has this functionality as well, but I didn't get far enough with rtorrent to have to worry about managing its currently running instance.

Config Management

After creating the service file and getting my script to start and stop the client when necessary, the next step was to be able to programatically set some of its config options (which is driven by a .rtorrent.rc file). A sample rtorrent config file can be found here.

Reading in the file and parsing out the options was not difficult at all, but preserving any comments in the file just gets messing. Having to read in the file, explode it by line endings, parse each line looking for the desired config option and re-writing that line just screamed that rtorrent wasn't really designed to be handled by an outside developer. Now there are some plugins for rtorrent that open up an API for this, but again, my evaluation of these clients were based on out-of-the-box functionalities.


rtorrent has the best command line interaction out of all the clients I've tested. Not only is its interface easy to navigate, but its 'add' functionality has tab-completion! After you get more than a dozen torrents or so, the default UI does become cumbersome with not being able to filter or search, rtorrent really does need an additional interface for management (which are available).


So after some work, I'm able to use a service file to 'daemonize' the client, I'm able to parse the config file and change a setting (IP address) as desired, but how was the human interaction? The default terminal UI for rtorrent is, by far, the best out of these options. It's fairly simple yet provides all of the information you'd really want to see on any given day. I was fairly happy with rtorrent, but I needed to try some other options before I settled down.


Searching the internet for torrent clients and talking with the average downloader, uTorrent may appear to be the most 'popular', but not only has been recently compromised, but it's also not ad-free by default. However, once you start digging around comparison posts and forums, Deluge always pops up as being a close second, if not first option, compared to Transmission.

Multi-instance Daemons

Its installation comes with a daemon (yay!) as well as deluge-console for initiating its TUI as well as issuing commands to a running daemon instance. Very promising.

So I quickly found that I can start a new instance by running the following command:

deluged --config INSTANCE_CONFIG_PATH --interface VPN_IP --pidfile PID_FILE

NOTE: I'm running multiple deluged instances by each config having its own unique port number in its core.conf file.

Wonderful. So I have multiple Deluge daemons running, all listening on my VPN's IP address, and I'm able to check if they're running by checking if the PID file exists.

That's where it gets tricky. In order to issue commands to the Deluge instance, you have to execute them through the deluge-console executable. That means I can execute the following command to stop an instance, right?

deluge-console --config INSTANCE_CONFIG_PATH halt

Wrong. Well, sort of. You can use that command to exit an instance. The issue with it comes when running multiple instances of deluged on the same machine. I found that executing the above command will in fact halt the deluge instance, but only the default deluge instance. Apparently passing in the config file to deluge-console only really gives it the auth credentials, not which port to communicate on, which is also in the config file.

So in order to solve this issue, you have to chain commands together.

deluge-console --config INSTANCE_CONFIG_PATH "connect localhost:PORT; halt"

Well, a bit of a PITA, but still able to be used in an automation script. So now I can have my script simply check for the instance's PID file, and if it exists and needs to quit the instance, issue the halt command, right?

Wrong. Well, again, sort of. I'm not really sure what is happening, but my guess is that deluge-console's halt command simply issues a kill command to the process. The downside here is that the PID file exists on the file system whether deluge is running or not.

Again, not a huge deal. I can simply use pgrep -F PID_FILE to tell me if its running, but still. I did dig into Deluge's code on GitHub, and Deluge does, in fact, clean up its PID file, but again, this probably assuming its exited gracefully, which my guess is the deluge-console command is just killing it off.

Config Management

Dude. Deluge's config files are JSON objects in an instance-specific directory, but the problem here is that the file doesn't contain valid JSON. Deluge's config file consists of two JSON objects back to back. The first one is seemingly useless to the user (it contains format and version information which, if left out after modifying, is added back anyway by deluge) and the second is actually a valid JSON object containing pretty much all of the config options you'd want.

Again, writing a parser for this isn't difficult, but it was just another hurdle to jump through. I mean, if you're going to make your config file an easily parsable format, why not make the entire thing valid syntax? Just separate the JSON objects with a comma (,) and wrap it in brackets ([])!

Anway, I did look through their source code that they are doing what I'm doing. Reading the file, splitting it on back-to-back curly braces, and then parsing it as JSON.


I won't lie. Deluge's default client being capable of a 'remote' mode that allows to you connect to any running daemon was amazing. If you install its GUI, you can go into the settings and disable 'classic mode'. This allows you to then connect via IP and port to a running instance and control it as if it were running locally on your machine. I got pretty excited.

That being said, with my server being headless, Deluge really falls short on its command line interaction. While deluge-console really has all the commands you'd want, it lacks in more than one place.

Inside the TUI, there is no tab-completion for adding new .torrent files. And when using the 'passthrough' method (i.e., deuge-console COMMAND), tab-completion is available (obviously, it's in your shell), but since all it is really doing is passing your arguments through, you have to double escape your paths!

So adding a torrent via your shell (with tab-completion) becomes either:

deluge-console add /home/user/my\\ torrent\\ files/ubuntu-16.10.torrent


deluge-console add "/home/user/my\ torrent\ files/ubuntu-16.10.torrent"

depending if you use quotes or not. So that's a pain.

Again, its default GUI also serving as a remote is wonderful, but since it reads all filesystem actions locally, you can't add torrent files that exist on your server via the remote interface.

It's also worth noting that a Deluge install also comes with deluge-web which is a pretty nice web interface for deluge, much like the native remote functionality, and this will allow you to browse the remote filesystem when adding torrents.


I really wanted to like Deluge. Probably more than any of the clients I've tried. I even talked myself into building my own CLI wrapper for deluge-console that would auto-double-escape my paths for me when adding torrents via the command line. But after all that, deluge did start to get laggy after adding multiple hundreds of torrents. Not unusable, but nowhere near the performance of other clients. And that isn't really Deluge's fault, its totally understandable. It's written in Python vs rtorrent and transmission are written in C++ and C. It's just a side effect of the technology.


Let's be honest. You know I ended up back with Transmission since it's the last one on the list. But it wasn't the perfect choice. It had its own issues. But the issues I did have with it paled in comparison with the other clients. (Again, this is issues I ran into trying to fit all of these clients into my above workflow. YMMV).

Multi-instance Daemons

Transmission is a daemon. Whether you're running it headless via transmission-daemon or the GUI, its a daemon. And running multiple instances of transmission is as simple as passing in the location to its config directory.

transmission-daemon -g CONFIG_DIR -x PID_FILE

Now transmission also comes with its own CLI for communicating with an instance. Simply pass the port in as the first argument for the instance you want to talk to:

transmission-remote 9091 --list

Simple enough.

Config Management

Transmissions config is a JSON object. Unlike Deluge, the entire file contains valid JSON. So manipulating its config is as simple as reading in the files contents and parse it with whatever function your language of choice has.

But again, it's not perfect. My issue with Transmission is that its config file doesn't contain, by default, all of the available options so it can be a little misleading on how configurable Transmission really is. For example, I was surprised to see there was no option for a watch directory in Transmission. Some quick Googling tells you that there is indeed an option to specify the watch directory and also whether its enabled or not. You just simply have to add it yourself.


The CLI for managing Transmission is wonderful. You pass in --exit to quit the daemon (which removes the PID file, by the way). Tell it --add TORRENT_FILE to add a torrent file without having to escape an characters twice. But the CLI is lacking when it comes to viewing and navigating around your downloads. The --list command is fine, and its easy to pipe to grep to filter out what you want, but there is no searching or filtering based on status. Also you can't perform bulk operations via the command line (in Deluge, you can pass a wildcard (*) to apply a command to all torrents). And there is no interactive TUI like rtorrents.


Transmission is very simple in what it does, but it does it really well. It might be missing a few bells and whistles that other clients may have (primarily labels or groups), but it stands its own with how it's built.


Your mileage may vary. I ended up with Transmission because it was easy to manage multiple daemon instances, it has an efficient way to control it via the command line, and its configuration file is super easy to manipulate via programming. Although its interface was lacking in comparison to the others (rtorrent's amazing TUI and Deluge's native client being a remote as well), but I did manage to find a great cross-platform app that does the same thing as Deluge's remote and it adds all the filtering, searching, and GUI management you could ask for (except for groups).