JOAL fakes a torrent client's upload stats to hit a ratio target
An open source command line RatioMaster with an optional WebUI.
At a glance
- What is it?
- JOAL is a Java server that emulates a BitTorrent client well enough for private trackers to accept its announcements, so you can reach a share ratio without keeping every torrent running, and it ships as a jar or a Docker image with an optional web interface.
- Who is it for?
- JOAL suits a user of a private tracker who wants to stop seeding large torrents while keeping the ratio and seeding requirements satisfied, and it suits someone who already runs Docker and wants a self-hosted service rather than a desktop app.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The trick is impersonating a torrent client to the tracker
JOAL is described as an open source command line RatioMaster with an optional WebUI, and the mechanism is worth understanding before the feature list. Private trackers do not check what you are actually uploading, they check what your client reports. The tracker expects periodic HTTP announcements containing upload and download totals, a ratio and a client identity, and it believes them. JOAL produces those announcements itself, using a .client file that identifies it as a specific real client such as qbittorrent, so a torrent you have stopped seeding can continue to report progress towards the tracker's ratio requirement. That is the whole idea. The repository carries a disclaimer that the tool is not designed to help or encourage downloading illegal materials and that the user must respect the law of their country, and there is a separate desktop project for the local variant. The audience is private tracker users who want the ratio accounting handled for them.
The client emulation table tells you what is realistic
The README includes a table of which BitTorrent clients JOAL can emulate, and it is worth reading as a compatibility list rather than a feature list. BitTorrent, Deluge, qBittorrent, rTorrent, Transmission, µTorrent and both Vuze flavours, Azureus and Leap, are all marked as supported. BitComet is marked as something that will never be supported, which is a specific refusal rather than an omission, and the README says to ask in GitHub issues or by mail if your client is missing, with the BitComet exception. The practical reason a client list matters is that trackers sometimes keep a list of known client binaries and block requests whose user agent or handshake does not match a real release of that client. A .client file in the joal-conf/clients/ directory is the piece that carries that identity, and the config.json names which one to use, so matching your real client is the difference between announcements being accepted and being ignored.
Configuration is one JSON file and a folder of torrents
The layout is deliberately flat. You download the latest tar.gz release, extract config.json, the clients folder and the torrents folder, and that becomes your joal-conf directory.
{
"minUploadRate" : 30,
"maxUploadRate" : 160,
"simultaneousSeed" : 20,
"client" : "qbittorrent-3.3.16.client",
"keepTorrentWithZeroLeechers" : true,
"uploadRatioTarget": -1.0
}The two rate fields are in kB/s and set the fake upload speed, the minimum and the maximum, which is how the tool looks like a real client rather than a constant. simultaneousSeed caps how many torrents report as seeding at once. keepTorrentWithZeroLeechers decides whether a torrent with no peers is kept at 0 kB/s or deleted when it reaches zero peers. uploadRatioTarget is the most consequential setting: when JOAL has uploaded a given multiple of the torrent size within a single session, the torrent is removed, and a value of -1.0 means torrents are never removed. Seeding is started by dropping .torrent files into the torrents folder, and there is no need to restart, since JOAL notices new files after a few seconds.
Running it is a jar or a container, and the web UI needs two secrets
With Java, the application is a single jar and one required argument.
java -jar ./jack-of-all-trades-X.X.X.jar --joal-conf="PATH_TO_CONF"The web interface is off by default and turned on with Spring arguments, and two of them are the security story. --joal.ui.path.prefix sets a path that must contain only alphanumeric characters, and the README says this might seem useless but is actually crucial to set it as complex as possible, to prevent people from knowing that JOAL is running on your server. --joal.ui.secret-token is a password for the interface. The interface is then at the prefix followed by /ui/. The Docker route is the same thing with a port mapping and a volume for the configuration, and the compose form sets restart: unless-stopped with the same arguments in a command array. The image on Docker Hub is anthonyraymond/joal, and the Dockerfile in the repository builds with Maven on a JDK image and ships the jar into a JRE-only image, with /data as a volume and the entrypoint set to run the jar with --joal-conf=/data.
Proxying the announcements is a one variable change
JOAL can route its traffic, meaning tracker announcements and IP checks, through an HTTP or HTTPS proxy, and the README says this is useful if you run it behind a VPN container such as Gluetun or want to hide your real IP address. The mechanism is unremarkable Java, which is a virtue here: you set JAVA_TOOL_OPTIONS with the standard proxy properties and it applies.
docker run -d \
-p PORT:PORT \
-v PATH_TO_CONF:/data \
-e "JAVA_TOOL_OPTIONS=-Dhttp.proxyHost=10.10.10.10 -Dhttp.proxyPort=8888 -Dhttp.nonProxyHosts=localhost|127.*" \
--name="joal" \
anthonyraymond/joal:X.X.X \
--joal-conf="/data" \
--spring.main.web-environment=true \
--server.port="PORT" \
--joal.ui.path.prefix="SECRET_OBFUSCATION_PATH" \
--joal.ui.secret-token="SECRET_TOKEN" \The one trap the README spells out is http.nonProxyHosts, which must include localhost, otherwise the internal web interface becomes unreachable, since the proxy intercepts it too. That is a five minute fix once you know, and an hour if you do not.
What this is not
The limitation is the obvious one and worth stating plainly. The mechanism only works against a tracker that trusts client announcements, which is the private tracker model, and against a tracker that does not cross check the announced upload volume against anything else. A public tracker has no ratio requirement to satisfy and no per user seeding requirement, so there is nothing here to gain. The second limitation is that the accounting is only as good as the numbers the tracker accepts, and trackers do check: a client announcing an implausible upload rate, a sudden jump, or an identity inconsistent with its reported version can be flagged, and repeated inconsistency can get an account banned. The third is scope. The Java project here is the server, the desktop application is a separate repository, and the web UI is described as optional rather than as the main interface, so a user who wants a local app is looking at a different codebase. The version history also shows uneven pacing, with 2.1.37 from 2025-12-20 following 2.1.36 from 2023-11-02, so a large gap sits between those tags while the last push was on 2026-10-01.
Editorial conclusion
JOAL suits a user of a private tracker who wants to stop seeding large torrents while keeping the ratio and seeding requirements satisfied, and it suits someone who already runs Docker and wants a self-hosted service rather than a desktop app. It is the wrong tool for a public tracker, where there is nothing to fake, and it is the wrong tool for anyone whose use of a tracker would violate its rules, since the entire mechanism is producing upload statistics for data you are not actually uploading. Before running it, read the config.json options, particularly uploadRatioTarget and keepTorrentWithZeroLeechers, because those two decide when a torrent is dropped, and set the path prefix and secret token to something nobody can guess, since the README is explicit that obscurity is the first layer of protection. The last push was on 2026-10-01 and the current release is 2.1.37 from 2025-12-20.
Frequently asked questions
What is JOAL and what does it do?
JOAL is an open source command line RatioMaster with an optional web interface. It emulates a BitTorrent client so that private trackers accept its announcements, which lets it report upload progress for torrents you are not actually seeding.
Which torrent clients can JOAL emulate?
The README lists BitTorrent, Deluge, qBittorrent, rTorrent, Transmission, µTorrent, Vuze Azureus and Vuze Leap as supported, with BitComet explicitly stated as never supported. The client identity is chosen through the client field in config.json, which names a .client file in the joal-conf/clients/ directory.
How do I run JOAL with Docker?
Run the anthonyraymond/joal image with the configuration directory mounted at /data and pass --joal-conf=/data, plus --spring.main.web-environment=true, --server.port, --joal.ui.path.prefix and --joal.ui.secret-token to enable the web interface. A docker-compose service with the same arguments and restart: unless-stopped is also given in the README.
What does uploadRatioTarget do in the JOAL config?
When JOAL has uploaded a given multiple of the torrent size in a single session, the torrent is removed. A value of -1.0 means torrents are never removed. The setting is separate from keepTorrentWithZeroLeechers, which decides whether a torrent with no peers is kept at 0 kB/s or deleted.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/anthonyraymond-joal)Was this useful?