Repository navigation
Scheduling
qbt-rules can schedule its own recurring jobs internally — you don't need an external cron job, systemd timer, or sidecar container hitting the HTTP API on a schedule anymore (though that pattern still works fine if you prefer it; see Contexts for that approach).
A new Scheduler component runs inside the server process, structurally similar to the background Worker — its own thread, checking a list of cron-style entries on a poll interval and enqueuing a job directly into the same queue every other job submission path uses (the HTTP API, the CLI client) when an entry's cron expression fires.
Add a schedule: list to config.yml, alongside server:/queue::
schedule:
- cron: "*/30 * * * *" # Every 30 minutes
context: cron
- cron: "0 3 * * *" # Daily at 3 AM
context: nightlyEach entry needs:
| Field | Description |
|---|---|
cron |
A standard 5-field cron expression (minute, hour, day-of-month, month, day-of-week) |
context |
The context string to submit — matched against rules' context: field exactly like any other job, see Contexts
|
Invalid entries (missing fields, an unparseable cron expression) fail server startup with a clear error rather than silently being skipped.
Environment variable override — since this is a list rather than a single value, it doesn't use a single QBT_RULES_SCHEDULE_* variable the way scalar config does. Instead, each entry is set via a pair of indexed variables, QBT_RULES_SCHEDULE_<N>_CRON and QBT_RULES_SCHEDULE_<N>_CONTEXT (each with the usual _FILE variant), starting at N=0 and counting up contiguously — enumeration stops at the first missing index:
QBT_RULES_SCHEDULE_0_CRON="*/30 * * * *"
QBT_RULES_SCHEDULE_0_CONTEXT=cron
QBT_RULES_SCHEDULE_1_CRON="0 3 * * *"
QBT_RULES_SCHEDULE_1_CONTEXT=nightlyIf any QBT_RULES_SCHEDULE_0_CRON is set, the environment-defined schedule entirely replaces config.yml's schedule: list — the two aren't merged, consistent with how every other field's environment variable overrides its config.yml value outright. Setting _0_CRON without a matching _0_CONTEXT (or vice versa) fails server startup with a clear error, same as a malformed config.yml entry.
It's also not hot-reloaded: unlike rules.yml, changes require a server restart to take effect (matching the rest of config.yml, which is only read once at startup).
Firing a scheduled entry is identical to a job submitted any other way — same queue, same worker, same job lifecycle (pending → processing → completed/failed), visible via --list-jobs/--job-status or the HTTP API Reference's /api/jobs endpoints. There's no separate "scheduled job" concept to track.
There's no misfire catch-up: if the server was down when a fire time passed, that occurrence is simply skipped — the same behavior an external cron job would have had.
If you've set server.workers above 1 (see In-Depth Configuration), the scheduler still fires exactly once per cron tick, not once per worker. It's started before Gunicorn forks its worker processes and deliberately isn't restarted per-fork the way the background worker thread is — so it keeps running in the long-lived master process regardless of how many request-handling workers you've configured. You don't need to do anything to get this behavior; it's automatic.
Metrics runs into the same category of problem (a naive in-process counter forked into unsynchronized per-worker copies) and solves it with a different mechanism (prometheus_client's multiprocess mode, since counters — unlike the scheduler — genuinely need to run in every worker, not just the master).
If you're currently running something like:
*/30 * * * * curl -s -X POST -H "X-API-KEY: $QBT_RULES_API_KEY" "http://qbt-rules:5000/api/execute?context=cron"The equivalent internal schedule entry is:
schedule:
- cron: "*/30 * * * *"
context: cronYou can remove the external cron job (or sidecar container) once this is in place and confirmed working — check --list-jobs after the first expected fire time to confirm.
qbt-rules | Client-server automation engine for qBittorrent
GitHub • Docker Image • Releases • Issues • Discussions • Contributing • License
Requirements: Docker • qBittorrent with Web UI enabled
Released into the public domain under the Unlicense
- Rules Architecture
- Contexts
- Conditions
- Actions
- Available Fields
- Reusable References
- HTTP API Reference
- Dashboard
- Metrics
- 🚀 Client-server architecture (HTTP API + worker)
- 📦 Persistent job queue (SQLite or Redis)
- ♻️ Reusable references (
$ref,${vars.x}) - 🐳 Docker-first deployment