Self-hosted service
crazy-max/docker-rtorrent-rutorrent avatar
crazy-max/docker-rtorrent-rutorrent

crazy-max/rtorrent-rutorrent packages rTorrent and ruTorrent into one Alpine image

rTorrent and ruTorrent Docker image

656 stars125 forksDockerfileMIT

At a glance

What is it?
This image bundles rTorrent compiled from source with the ruTorrent web interface, nginx in front of it, XMLRPC over an SCGI socket, WebDAV for completed downloads and a non-root user, published for four Linux platforms on Docker Hub and GitHub Container Registry. The price of the convenience is a stack where the C++, PHP and web server configuration all live in one repository.
Who is it for?
Adopt crazymax/rtorrent-rutorrent if you want a self-hosted rTorrent with a web interface on a small footprint, and you would rather pull a maintained image than compile rTorrent, libtorrent and ruTorrent yourself.
Can I use it commercially?
Yes. MIT 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 received new commits within the last day.
What is it written in?
Mainly Dockerfile, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the image actually packages

This is a Docker image for rTorrent with ruTorrent, from the crazy-max organisation, and the interesting part of what it packages is what is compiled from source rather than installed from packages.

The README states that the latest rTorrent and libTorrent release is compiled from source, and the latest ruTorrent release is included. The Dockerfile confirms it in build arguments: LIBTORRENT_VERSION and RTORRENT_VERSION are both v0.16.25, RUTORRENT_VERSION is v5.3.16, and there are separate stages for c-ares at 1.34.8, curl at 8.21.0, mktorrent at v1.1 and DumpTorrent at v1.7.0. The base is Alpine, with ALPINE_VERSION set to 3.24, and the build runs in a src stage that adds curl, git, tar, tree, sed and xz, then fetches each project with git fetch and checks out FETCH_HEAD.

Two of those deserve comment. mktorrent is compiled from source for the ruTorrent create plugin, and DumpTorrent is compiled for the ruTorrent dump plugin, so the image includes the helper binaries those plugins shell out to rather than expecting them on the host.

The other components are the ones a user actually touches. nginx sits in front and provides both the ruTorrent web interface and an XMLRPC endpoint over an SCGI socket, with basic auth optional. WebDAV is served on completed downloads, also with optional basic auth. There is a vendored ruTorrent GeoIP2 plugin directory, and the feature list names a geoip-updater image for downloading MaxMind databases on a schedule, and a Traefik template for running the whole thing behind a reverse proxy that handles Let's Encrypt certificate creation and renewal.

The base image itself is Alpine, which is why the image is small, and the ruTorrent interface is a PHP application served by PHP-FPM, which is why the environment list contains PHP memory and upload settings.

Non-root, multi-platform and the images you can pull

The first feature in the list is that the container runs as a non-root user, and the environment variables are how you control that. PUID sets the rTorrent user id and PGID the group id, both defaulting to 1000.

The images are published in two places. The Docker Hub entry is crazymax/rtorrent-rutorrent, and the GitHub Container Registry entry is ghcr.io/crazy-max/rtorrent-rutorrent.

The platform list comes from the manifest rather than from marketing, and the README shows the exact command used to read it:

shell
$ docker buildx imagetools inspect crazymax/rtorrent-rutorrent --format "{{json .Manifest}}" | \
  jq -r '.manifests[] | select(.platform.os != null and .platform.os != "unknown") | .platform | "\(.os)/\(.architecture)\(if .variant then "/" + .variant else "" end)"'

The result is four platforms: linux/amd64, linux/arm/v6, linux/arm/v7 and linux/arm64. The arm variants are what makes this usable on a Raspberry Pi or an ARM server, and the v6 and v7 entries cover the older 32-bit boards that a single arm64 build would miss.

The release tags encode the versions. The most recent listed is 5.3.16-0.16.25-r0, which pairs ruTorrent 5.3.16 with rTorrent and libTorrent 0.16.25 and a build revision of zero. The earlier tags follow the same pattern, 5.3.14-0.16.22-r0 and 5.3.12-0.16.21-r0. The r suffix is a useful signal for an operator, because it tells you the image was rebuilt without changing the upstream versions, which is how a base image security update ships without moving the application.

The last push was on 2026-10-21 and the repository is not archived, so the image is being rebuilt regularly.

Building the image locally with buildx bake

The build is driven by a bake file, docker-bake.hcl, and the README gives three commands, which is the whole build story.

shell
git clone https://github.com/crazy-max/docker-rtorrent-rutorrent.git
cd docker-rtorrent-rutorrent

# Build image and output to docker (default)
docker buildx bake

# Build image
docker buildx bake image

# Build multi-platform image
docker buildx bake image-all

The default target produces the image and outputs it to Docker, the image target builds the single platform image, and image-all builds the multi-platform one. The distinction between the second and third target is the usual one, and it matters for local iteration: a single platform build is quick and is what you want while changing something in the rootfs, while the multi-platform build is what produces the manifest list the published image uses.

The Dockerfile is a multi-stage build, and the stages are named after what they produce. A gosu stage comes from tianon/gosu, a src stage is built for the build platform so that the compilation happens on the builder rather than under emulation, and there are then separate src-cares, src-curl, src-libtorrent, src-rtorrent, src-mktorrent and src-rutorrent stages that each fetch and prepare one dependency. The ruTorrent stage removes the .git directories along with conf/users, plugins/geoip, share and tests, which is the standard way to strip what should not ship. There is also an update-geoip2-rutorrent stage built from composer:2 that installs the vendored GeoIP2 plugin with a classmap authoritative composer update, so the plugin is present as a dependency rather than a loose directory.

The repository layout supports this design. There is a rootfs directory holding the filesystem that gets copied into the image, including the tpls directory with the enhanced rTorrent config file and the .rtlocal.rc local config that bootstraps it. There is a geoip2-rutorrent directory for the vendored plugin, an examples directory with compose and traefik subdirectories, and a test directory.

The environment variables and the config templates behind them

The environment variable list is the primary knob, and the README documents it in groups: General, rTorrent, ruTorrent, then Volumes and Ports.

The general group covers container and web server tuning. TZ is the timezone assigned to the container, defaulting to UTC. MEMORY_LIMIT is the PHP memory limit, defaulting to 256M. UPLOAD_MAX_SIZE is the upload maximum, defaulting to 16M, and MAX_FILE_UPLOADS is the number of files that can be uploaded at once, defaulting to 50. OPCACHE_MEM_SIZE sets the PHP OpCache memory consumption, defaulting to 128, and CLEAR_ENV clears the environment in FPM workers, defaulting to yes.

The nginx-related variables are the ones that matter behind a reverse proxy. NGINX_WORKER_PROCESSES sets the number of nginx worker processes and defaults to auto. REAL_IP_FROM is the trusted addresses known to send correct replacement addresses, defaulting to 0.0.0.0/32, and REAL_IP_HEADER is the request header field whose value replaces the client address, defaulting to X-Forwarded-For. AUTH_DELAY is the time in seconds to wait for Basic Auth, defaulting to 0s. Get these wrong behind Traefik and the access log and the rate limiting both see the proxy's address rather than the client's.

Two variables are for the tracker rather than the server. WAN_IP is the public IP address reported to the tracker, and it is automatic if left empty, and WAN_IP_CMD is the command to resolve that address. This is the setting that stops a client behind a NAT or a VPN from announcing an address the tracker cannot reach, and a wrong value here produces trackers that reject or ignore the client.

The README's table of contents lists further sections on XMLRPC through nginx, WebDAV, populating .htpasswd files, checking the ruTorrent environment, the bootstrap config .rtlocal.rc, overriding or adding a ruTorrent plugin or theme, editing a ruTorrent plugin configuration, increasing the Docker timeout so rTorrent shuts down gracefully, the WAN IP address, and configuring rTorrent session saving, tracker scrape, send and receive buffers and disk space preallocation. That list is the honest shape of this image: the environment variables are the friendly surface, and the rTorrent configuration templates under rootfs/tpls are where the real tuning lives.

ruTorrent needs more than ruTorrent

A ruTorrent instance is a PHP application that expects a fastcgi process manager, a web server, a writable configuration area and a set of plugins with their own settings, and the image's real work is in making all of that agree.

The plugin situation is the part that surprises people. The features list includes the ability to add a custom ruTorrent plugin or theme, and the ability to persist specific configuration for ruTorrent plugins. The mechanism is the bootstrap config, the.rtlocal.rc file, which is applied at start so that plugin settings survive a container restart. The README also documents editing a ruTorrent plugin configuration and overriding or adding a plugin or theme, which means the image expects you to mount files into the image's plugin directories.

There is a note on checking the ruTorrent environment, which is a diagnostic step rather than a feature, and the existence of that note tells you something about the failure modes. A ruTorrent that renders but cannot connect to rTorrent, or a ruTorrent that errors on a plugin, is a common enough problem that the image ships a way to inspect it.

The XMLRPC path is the other half. ruTorrent talks to rTorrent over XMLRPC, and the image routes that through nginx over an SCGI socket rather than exposing rTorrent's own XMLRPC port directly, with basic auth optional on the nginx side. That indirection is what makes the reverse proxy template work, and and a reverse proxy needs to be configured accordingly for the ruTorrent interface to keep working.

The WebDAV endpoint is scoped deliberately, to completed downloads, and is also protected by optional basic auth. That is a genuinely useful design decision for a torrent client, because it gives you a way to fetch finished files from a phone without exposing the session directory or the whole interface.

Where this image is the wrong choice

There are three honest objections, and none of them is about the image being bad.

The first is auditability. The image contains rTorrent and libtorrent compiled from source, ruTorrent from source, nginx, PHP-FPM and a set of plugins, with a vendored GeoIP2 plugin installed through composer. That is a lot of supply chain in one artefact, and while the version pinning in the build arguments is good practice, a security review of the resulting image is not a small task. A team with a strict policy about what runs in production may prefer a distro package base with fewer moving parts, accepting an older rTorrent.

The second is that the interface is not the only way in. ruTorrent is a PHP web interface, and a PHP web interface needs a web server, a PHP process manager, a session directory and careful handling of uploads. If you are exposing this to the internet, you are running a PHP application with a long history of vulnerabilities next to a BitTorrent client, and the image's mitigations are the ones you would expect from a well run project, but they are mitigations rather than elimination. The WebDAV scoping and the optional basic auth are the right defaults; the responsibility for terminating TLS and running a reverse proxy is left to the Traefik template or to you.

The third is the rTorrent project itself. rTorrent is long-established and stable, and stability is exactly the property a torrent client needs, but it is a C++ codebase with a configuration model based on a.rtorrent.rc file, and the image's enhanced config and local bootstrap config are deliberate modifications of it. If you need to understand precisely what your client is doing, you now have a patched configuration to read as well as rTorrent's own documentation.

The alternative approach is to run rTorrent directly on a host, with the ruTorrent interface deployed separately, or to use a different client entirely such as Transmission or Deluge, both of which have their own Docker images and their own web interfaces. The difference in approach is that this image optimises for a single, pre-integrated, small footprint: one container with a bundled web stack, many environment variables, and a set of templates that the maintainer has already reconciled. A separate ruTorrent deployment gives you independent upgrade paths and a web stack you control, at the cost of more moving parts.

Editorial conclusion

Adopt crazymax/rtorrent-rutorrent if you want a self-hosted rTorrent with a web interface on a small footprint, and you would rather pull a maintained image than compile rTorrent, libtorrent and ruTorrent yourself. Do not adopt it if you need an image you can audit end to end, since rTorrent, libtorrent, ruTorrent, nginx and PHP are all compiled or installed inside it, and do not assume the environment variables are a complete configuration surface, because the documented list is partial and the rest lives in the bundled rTorrent config templates. Verify first that the tag you pin matches the rTorrent and ruTorrent versions you intend to run, that the XMLRPC and WebDAV endpoints are protected with the basic auth options the README describes, that the PUID and PGID you set match the ownership of the download directory, and that your reverse proxy is configured for the WebSocket traffic ruTorrent needs.

Frequently asked questions

What does the crazymax/rtorrent-rutorrent image contain?

It runs rTorrent with the ruTorrent web interface. The README states that the latest rTorrent and libTorrent release is compiled from source along with the latest ruTorrent release, with nginx in front, XMLRPC over an SCGI socket, WebDAV on completed downloads, mktorrent and DumpTorrent compiled for the ruTorrent plugins, a vendored GeoIP2 plugin, and a non-root user.

Which platforms does the rtorrent-rutorrent image support?

The manifest lists linux/amd64, linux/arm/v6, linux/arm/v7 and linux/arm64, which the README shows how to read directly with docker buildx imagetools inspect and jq. The images are published on Docker Hub as crazymax/rtorrent-rutorrent and on GitHub Container Registry as ghcr.io/crazy-max/rtorrent-rutorrent.

How do I build the image locally?

Clone the repository and use buildx bake. The README gives three commands: docker buildx bake to build and output to docker by default, docker buildx bake image for a single platform image, and docker buildx bake image-all for the multi-platform image. The build is driven by docker-bake.hcl.

What do the PUID and PGID environment variables do?

They set the rTorrent user id and group id inside the container, both defaulting to 1000, and they determine which user owns the downloaded files on the mounted volume. The image runs as a non-root user, so these identifiers are how the container's process maps onto your host's user.

How is the WAN_IP variable used in the rtorrent-rutorrent image?

WAN_IP is the public IP address reported to the tracker, and it is automatic if left empty, while WAN_IP_CMD is the command to resolve that address. Leaving the auto-detection in place matters because a client that announces an unreachable address will be ignored or rejected by trackers.

Official sources

  1. crazy-max/docker-rtorrent-rutorrent on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/crazy-max-docker-rtorrent-rutorrent.svg)](https://hysenlabs.com/projects/crazy-max-docker-rtorrent-rutorrent)

Was this useful?