Stewarrd

Stewarrd

Stewarrd brings qBittorrent, RDT-Client, SABnzbd and RQBit together in one authenticated web interface and API, alongside the Sonarr and Radarr instances that use them. It shows what every download is doing, explains why one is stuck, and can repair common failures once you have reviewed what it would do.

Documentation · Automation guide · Contributing · Security · MIT license

What it does

Every recovery, import and cleanup policy starts in preview: it records what it would do without doing it. Stewarrd does not choose media for Arr, transfer files itself, enforce seeding rules, clean up orphaned files, or manage hosts and containers. The automation guide describes each policy's requirements and limits.

Install with Docker Compose

You need Docker Engine with BuildKit, Docker Compose v2, an x86-64 Linux host, and network access from the container to your clients and Arr applications.

git clone https://github.com/jamtur01/stewarrd.git
cd stewarrd
cp .env.example .env    # add the credentials for the connections you will use
docker compose up -d --build
docker compose exec stewarrd cat /data/access-token

Open http://localhost:8787 and sign in with that token. Keep it private: it authorizes both the web interface and the API. The included Compose file builds the checked-out source, listens on loopback only, runs read-only as UID/GID 1000 with no added capabilities, and keeps the token and SQLite database in the stewarrd-data volume.

Prebuilt images are published to Docker Hub. To use one, replace build: . and image: stewarrd:local in compose.yaml with image: jamtur01/stewarrd:<version>, then run docker compose pull && docker compose up -d.

Connect clients and Arr applications

  1. In Connections, add each client instance: its type, a unique instance ID, a display name, and a base URL the Stewarrd container can reach. Enter the username where needed and the name of the environment variable that holds its password or API key.
  2. Use Test & refresh to confirm the connection.
  3. In Media, add Sonarr and Radarr the same way, then map each download client to the exact name it has in that Arr.
  4. Check that the queues look right before adding any Recovery, Imports or Cleanup policy.

Credentials

Credentials live in .env, never in the connection form. Each variable must start with STEWARRD_CLIENT_ and have a companion _URL variable that matches the connection's base URL exactly, including any reverse-proxy path. Stewarrd refuses to send a credential to any other address.

STEWARRD_CLIENT_SAB_API_KEY=replace-with-your-api-key
STEWARRD_CLIENT_SAB_API_KEY_URL=http://sabnzbd:8080

Here the form takes STEWARRD_CLIENT_SAB_API_KEY and http://sabnzbd:8080. After editing .env, run docker compose up -d --force-recreate; a plain restart does not reload it.

Networking

Container names resolve only on a shared Docker network. If your clients run in a separate Compose project, join Stewarrd to their network or use hostnames and published ports it can reach; localhost inside the container is Stewarrd itself. If a client's reverse proxy validates Host headers, configure its external host and port rather than turning the check off.

Client notes

Automation

Recovery, Imports and Cleanup each have per-client or per-Arr policies. A new policy is in preview: it reads client and Arr state and records what it would do, but sends nothing. Enabling a policy authorizes only that policy's documented writes. Manual controls in the interface always act immediately; there is no global dry-run switch.

Policy When enabled, it Also needs
Recovery Blocklists failed or stalled releases, removes them from the client while keeping files, and searches for replacements Read-only mounts of the Arr databases, plus RDT-Client's for RDT clients, and a network health endpoint
Imports Copy-imports downloads Arr blocked only for an ID match Read-only download mounts
Cleanup Removes downloads with unsafe files and rejects the release in Arr Read-only download mounts covering every client that shares storage, and the RDT-Client database for RDT

Add the mounts to the Compose service, keeping them read-only, for example:

volumes:
  - stewarrd-data:/data
  - /srv/downloads/complete:/downloads/complete:ro
  - /srv/rdt-client/db:/evidence/rdt:ro
  - /srv/sonarr/config:/evidence/sonarr:ro

Follow the automation guide for mappings, prerequisites and exactly what each policy does. Run only one automation tool against the same downloads at a time.

Notifications

Set STEWARRD_NOTIFICATION_URL to deliver these as JSON webhook events (see the automation guide for the payload). Each title names what happened and to which download; the detail names the client and Arr and gives the reason. Most conditions notify only after they persist for the delay shown, and send one event when they start. When a condition ends, a second event prefixes the title with Cleared: (the problem resolved) or Monitoring stopped: (Stewarrd stopped watching it: a recovery plan was discarded or took over, a policy was turned off, or a client or Arr was removed).

Notification After What it means What to do
<client> is unreachable 15 min Stewarrd cannot list that client's downloads. Check the client is running and its URL and credentials.
<Arr> is unreachable 15 min Stewarrd cannot read that Sonarr or Radarr. Check the Arr is running and its URL and API key.
<Arr> health <type>: <check> 5 min The Arr's own System → Status check reports a notice, warning or error, for example about an indexer or download client. Open System → Status in that Arr. Silence a check with STEWARRD_NOTIFICATION_IGNORE_HEALTH.
<Arr> download failed: <release> 15 min The Arr's queue marks the download failed; the detail gives the Arr's reason. Arr normally removes it and searches again itself. Look at the Arr queue if it does not move on.
<Arr> import blocked: <release> 30 min The download finished but the Arr cannot import it, for example a name it cannot match or no eligible files. Fix the reason given, or import it manually in the Arr.
<Arr> flagged unsafe content: <release> Immediately The Arr's own check found a potentially dangerous file, such as an executable. Remove the download; enabled cleanup does this for Arr-grabbed downloads.
<client> download failed: <release> 15 min The client reports the job failed, with its error. Not sent for SAB, whose failures Arr handles, or for downloads an enabled recovery plan is handling. Retry or remove it in the client.
<client> download stalled: <release> Stall window Stall recovery is on for that client but deliberately skips this transfer: a private torrent, qBittorrent before 5.0, or an unreadable RQBit torrent file. Decide by hand; Stewarrd will not remove it.
Recovery held: <release> 15 min A failed-release, stall or empty-record recovery plan stopped before finishing; the detail says why, for example Remove Failed is on, grab history is ambiguous or RDT auto-import is on. Fix the cause and the plan continues, or review it in the recovery journal.
Recovery checks failing: <client> → <Arr> 15 min Recovery cannot read that client, Arr, health endpoint or evidence database, so its stall timers restart. Check connectivity and the read-only mounts.
Import recovery held: <release> 15 or 30 min Copy-import recovery stopped on a safety check; the detail says which. Fix the cause or import it manually.
Import recovery checks failing: <Arr> 15 min Copy-import recovery cannot read that Arr or its downloads. Check the Arr connection and download mounts.
Unsafe content found: <release> Immediately, once Cleanup found unsafe files in a download, whether cleanup is enabled, in preview, or the download was not grabbed by an Arr. Review it under Cleanup.
Unsafe download returned: <release> Immediately A download cleanup already removed is back in the client with unsafe files; cleanup handles it again. Check why it was grabbed again.
Cleanup held: <release> Immediately Cleanup will not, or cannot yet, remove this download; the detail says why, for example a shared payload, protection or RDT auto-import. Fix the cause and cleanup retries next cycle. If the detail says a removal is unverified, review it under Cleanup.
Unsafe download removed: <release> When done Cleanup removed the download and rejected the release in the Arr. Nothing.
Cleanup checks failing: <Arr> queue 15 min Cleanup cannot read that Arr's queue and history. Check the Arr connection.
Cleanup checks failing: <client> files 15 min Cleanup cannot inspect that client's files. Check the client and its download and evidence mounts.

Configuration

Setting Default Purpose
STEWARRD_BIND_ADDRESS 127.0.0.1 Compose host address; use your LAN address for LAN access
STEWARRD_PORT 8787 Compose host port
PORT 8787 Internal server port; update the container-side port mapping if changed
STEWARRD_DATA_DIR /data in Docker Persistent application state
STEWARRD_ACCESS_TOKEN Generated in the data directory Optional operator/API token, at least 32 characters
STEWARRD_SECURE_COOKIE false Set to true when the application is served over HTTPS
STEWARRD_NOTIFICATION_URL Unset Generic JSON webhook; no delivery when unset
STEWARRD_NOTIFICATION_IGNORE_HEALTH Unset Comma-separated Arr health source names excluded from notifications
STEWARRD_DOWNLOADS_DIR /downloads Root for explicitly mounted download paths
STEWARRD_EVIDENCE_DIR /evidence Root for read-only evidence database directories

Running in production

Upgrades, backups and rollback

Stop Stewarrd before backing up its data volume, and keep the whole volume, including access-token, together with your Compose and .env files. Keep backups private, and never run docker compose down --volumes unless you mean to discard everything.

Read the release notes before upgrading. Older credential names must be renamed to STEWARRD_CLIENT_*, given _URL bindings, and updated in saved connections. Build or pull the chosen version, recreate the container, and check the connections with policies in preview.

To roll back, stop the service and restore both the previous version and the data backup taken with it; an older version may not read newer state. Changing the access token and recreating the service rotates access, and any restart signs out existing browser sessions.

Troubleshooting

API and development

API clients send Authorization: Bearer <access-token>. The authenticated GET /api/openapi.json documents the same operations the interface uses.

Stewarrd is built with Node 24, TypeScript, React, Fastify and SQLite. See CONTRIBUTING.md for local development and checks, and SECURITY.md to report a vulnerability privately. The project is MIT licensed; dependencies keep their own licenses.