A rules engine for qBittorrent automation, with a job queue, scheduler and web dashboard.
Write what should happen to your torrents in YAML, and qbt-rules keeps doing it: when a torrent is added, when it finishes, on a schedule, or whenever you ask. It categorizes, tags, pauses, resumes, deletes, throttles, skips files and notifies, so an unattended qBittorrent doesn't turn into uncategorized downloads, dead torrents and unmet ratio requirements.
rules:
- id: skip-sample-files
meta:
description: "Don't download sample files"
context: torrent-imported # run when qBittorrent adds a torrent
conditions:
all:
- field: files.name
operator: matches
value: '(?i)(^|/)sample'
actions:
- type: set_file_priority
params:
priority: skip
pattern: '(?i)(^|/)sample'- Declarative YAML rules, no coding required, run top to bottom in file order
- Reusable references: define a variable, condition, or action sequence once under
refs:, reuse it everywhere (docs) - Free-form execution contexts: webhooks, cron sweeps, the internal scheduler, manual runs; no fixed trigger list (docs)
- Rich conditions: AND/OR/NOT groups, 17+ operators, and fields from 8 qBittorrent API categories, plus fleet-wide
stats.*and optional tracker-sidetracker.*stats from trackie - Idempotent actions, safe to run repeatedly, and a dry-run mode to preview them
- Dashboard: jobs, results and rules at a glance, plus an in-browser
rules.ymleditor that validates as you type, on the same port as the API - Persistent job queue (SQLite or Redis): jobs survive restarts
- Multi-version qBittorrent support: v4.1 through v5.1+ via
qbittorrent-api's auto-detection
You need Docker and a qBittorrent with its Web UI enabled. Save this as compose.yml, set your qBittorrent
address and credentials, and replace change-me with a long random string (openssl rand -hex 32):
services:
qbt-rules:
image: ghcr.io/andronics/qbt-rules:latest
container_name: qbt-rules
restart: unless-stopped
ports: ["127.0.0.1:5000:5000"]
volumes: ["./config:/etc/qbt-rules:ro", "./state:/var/lib/qbt-rules"]
environment:
QBT_RULES_QBITTORRENT_HOST: http://qbittorrent:8080
QBT_RULES_QBITTORRENT_USERNAME: admin
QBT_RULES_QBITTORRENT_PASSWORD: adminpass
QBT_RULES_SERVER_API_KEY: change-me # API + dashboard login
QBT_RULES_DRY_RUN: "true" # log what rules would do; set false when happydocker compose up -d
curl http://127.0.0.1:5000/api/health
docker exec qbt-rules qbt-rules execute --context torrent-imported --wait # run the torrent-imported rules nowRules live in ./state/rules.yml (start from contrib/rules.example.yml, or
write them in the dashboard's editor at http://127.0.0.1:5000/rules/edit, logging in with the API key), beside
the job queue; ./config holds only the optional config.yaml and can be read-only. Nothing is copied in on first start, and every setting has an environment variable, so
config.yaml is optional. Read your rules before turning dry-run off. The container runs as PUID:PGID (default
1000:1000; Unraid uses 99/100).
Upgrading from a release that mounted
/config?config.yamlis now in/etc/qbt-rules, andrules.ymland the database in/var/lib/qbt-rules;/configis no longer read. The wiki's Migrating from 0.10.x lists which file goes where.
The full walk-through, including your first rule and triggering rules from qBittorrent's "Run external program" setting, is in the wiki's Quick Start. A hardened Compose file is in contrib/docker/compose.yml.
Everything is in the wiki. Its source is docs/wiki/, in this
repository, reviewed with the code.
- Introduction: what it is and how it works
- Rules Architecture, Contexts, Conditions, Actions and Available Fields: writing rules
- Examples and Advanced Rules Example
- In-Depth Configuration: every setting
- CLI Reference and HTTP API Reference
- Deployment: Docker Compose, Redis, upgrading
- Security: threat model, API auth, network exposure, credentials
- Troubleshooting and FAQ
- Contributing and Release Process
Built with AI assistants; every change gets human review. AGENTS.md and .agents/stack.md are the only instruction files (stack, commands, rules, definition of done, docs map). Read them instead of this README.
| Assistant | Entry point |
|---|---|
| Claude Code | CLAUDE.md (imports both) + .claude/settings.json |
| Codex, Cursor, Copilot coding agent | AGENTS.md, natively (Copilot, Cursor pointers) |
| Gemini CLI | GEMINI.md (imports both) |
| Chat without repo access | docs/ai/prompts.md, "Context for a chat assistant" |
Humans: kick-off prompts are in docs/ai/prompts.md, and the review checklist in docs/ai/review-checklist.md.
uv sync # Python >= 3.13, dev tools included
uv run pytest # tests (80% coverage floor)
uv run ruff check && uv run mypy # lint and type check
sh scripts/checks.sh # everything CI's checks job runsThe workflow is: a branch, Conventional Commit PR titles, squash merge. Releases are automated by release-please, which also writes the CHANGELOG. See Contributing.
This project was generated from template-base and template-python (and template-container for the image). Template fixes arrive as a weekly Template update PR per template.
Released into the public domain under the Unlicense.
