Skip to main content

Automations

Automations are a rule-based engine that automatically applies actions to torrents based on conditions. Use them to manage speed limits, delete old torrents, organize with tags and categories, and more.

How Automations Work

Automations are evaluated in sort order (first match wins for exclusive actions like delete). Each rule can match torrents using a flexible query builder with nested conditions.

  • Automatic - Background service scans torrents every 20 seconds
  • Per-Rule Intervals - Each rule can have its own interval (minimum 60 seconds, default 15 minutes)
  • Manual - Click "Apply Now" to trigger immediately (bypasses interval checks)
  • Manual dry-run - Run "Dry-run now" from the workflow dialog or "Run dry-run now" from the workflow menu
  • Debouncing - Same torrent won't be re-processed within 2 minutes

Query Builder

The query builder supports complex nested conditions with AND/OR groups. Drag conditions to reorder them.

Available Condition Fields

Identity Fields

FieldDescription
NameTorrent display name (supports cross-category operators)
HashInfo hash
Infohash v1BitTorrent v1 info hash
Infohash v2BitTorrent v2 info hash
Magnet URIMagnet link for the torrent
CategoryqBittorrent category
TagsSet-based tag matching
StateStatus filter (see State Values below)
Created ByTorrent creator metadata

Path Fields

FieldDescription
Save PathDownload location
Content PathFull path to content
Download PathSession download path from qBittorrent

Size Fields (bytes)

FieldDescription
SizeSelected file size
Total SizeTotal torrent size
CompletedCompleted bytes
DownloadedBytes downloaded
Downloaded (Session)Downloaded in current session
UploadedBytes uploaded
Uploaded (Session)Uploaded in current session
Amount LeftRemaining bytes
Free SpaceFree space on disk (configurable source - see Free Space Source)

Duration Fields (seconds)

FieldDescription
Added AgeTime since added
Completion AgeTime since completed
Inactive TimeTime since last activity
Seen Complete AgeTime since torrent was last complete
ETAEstimated time to completion
Reannounce InSeconds until next announce
Seeding TimeTime spent seeding
Time ActiveTotal active time
Max Seeding TimeConfigured max seeding time
Max Inactive Seeding TimeConfigured max inactive seeding time
Seeding Time LimitTorrent seeding time limit
Inactive Seeding Time LimitTorrent inactive seeding time limit

Progress Fields

FieldDescription
RatioUpload/download ratio
Ratio LimitConfigured ratio limit
Max RatioqBittorrent max ratio value
ProgressDownload progress (0-100%)
AvailabilityDistributed copies available
PopularitySwarm popularity metric

Speed Fields (bytes/s)

FieldDescription
Download SpeedCurrent download speed
Upload SpeedCurrent upload speed
Download LimitConfigured download speed limit
Upload LimitConfigured upload speed limit

Peer/Queue Fields

FieldDescription
Active SeedersCurrently connected seeders
Active LeechersCurrently connected leechers
Total SeedersTracker-reported seeders
Total LeechersTracker-reported leechers
Trackers CountNumber of trackers
Queue PriorityTorrent queue priority value

Tracker/Status Fields

FieldDescription
TrackerPrimary tracker (URL, domain, or customization display name)
Trackers (All)All tracker URLs/domains/display names for this torrent
PrivateBoolean - is private tracker
Is UnregisteredBoolean - tracker reports unregistered
CommentTorrent comment field

Note: if you have Settings → Tracker Customizations configured, the Tracker condition can match the display name in addition to the raw URL/domain.

Mode Fields

FieldDescription
Auto-managedManaged by automatic torrent management
First/Last Piece PriorityFirst and last pieces are prioritized
Force StartIgnores queue limits and starts immediately
Sequential DownloadDownloads pieces sequentially
Super SeedingSuper-seeding mode enabled

Release/Grouping Fields

FieldDescription
Content TypeDerived from release name parsing (useful for grouping; may be empty)
Effective NameNormalized title derived from release parsing (useful for grouping; may be empty)
Release SourceParsed release specifier (e.g. WEBDL, WEBRIP, BLURAY; may be empty)
Release ResolutionParsed release specifier (e.g. 1080p; may be empty)
Release CodecParsed release specifier (e.g. HEVC; may be empty)
Release HDRParsed release specifier (e.g. DV, HDR; may be empty)
Release AudioParsed release specifier (e.g. TrueHD; may be empty)
Release ChannelsParsed release specifier (e.g. 5.1; may be empty)
Release GroupParsed release specifier (e.g. NTb; may be empty)
Group SizeSize of the selected group for this condition (requires grouping; see Grouping)
Is GroupedBoolean - true when selected group size > 1 (requires grouping; see Grouping)

Filesystem Fields

FieldDescription
Hardlink Scopenone, torrents_only, or outside_qbittorrent (requires local filesystem access)
Has Missing FilesBoolean - completed torrent has files missing on disk (requires local filesystem access)

State Values

The State field matches these status buckets:

StateDescription
downloadingActively downloading
uploadingActively uploading
completedDownload finished
stoppedPaused by user
activeHas transfer activity
inactiveNo current activity
runningNot paused
stalledNo peers available
stalled_uploadingStalled while uploading
stalled_downloadingStalled while downloading
erroredHas errors
tracker_downTracker unreachable
checkingVerifying files
checkingResumeDataChecking resume data
movingMoving files
missingFilesFiles not found
unregisteredTracker reports unregistered

Operators

String: equals, not equals, contains, not contains, starts with, ends with, matches regex

Numeric: =, !=, >, >=, <, <=, between

Boolean: is, is not

State: is, is not

Cross-Category (Name field only):

  • EXISTS_IN - Exact name match in target category
  • CONTAINS_IN - Partial/normalized name match in target category

Regex Support

There are two ways to use regex in filter conditions:

The matches regex operator is a dedicated operator where the value is always treated as a regex pattern. The condition is true if the pattern matches anywhere in the field value.

The regex toggle (.* button) appears next to the value input on other string operators such as equals, contains, not contains, starts with, and ends with. When enabled, the value is treated as a regex pattern.

Regex toggle overrides the selected operator

When the regex toggle is enabled, the selected operator's logic (negation, containment, prefix/suffix matching) is not applied. The condition becomes a simple regex match, equivalent to matches regex, regardless of which operator is selected in the dropdown.

This means not contains with the regex toggle enabled does not negate the match. It behaves the same as matches regex -- if the pattern is found, the condition evaluates to true.

To negate a regex match, use the NOT toggle (the IF / IF NOT button at the start of the condition row) together with the matches regex operator.

Full RE2 (Go regex) syntax is supported. Patterns are case-insensitive by default.

Regex can be used either by selecting matches regex or by enabling the Regex toggle for a condition:

  • When regex is enabled, the condition checks whether the regex matches the field value.
  • not equals and not contains invert the regex result (true only if the regex does not match).
  • Operators like equals, contains, starts with, and ends with are treated as regex match when regex is enabled.
  • Regex is not implicitly anchored: use ^ and $ if you want an exact/full-string match (example: ^BHD$).

Field notes:

  • Tracker: checked against multiple candidates (raw URL, extracted domain, and optional customization display name). Negative regex passes only if none of the candidates match.
  • Tags: without regex, string operators are applied per-tag. With regex enabled, the regex is matched against the full raw tags string.

The UI validates patterns and shows helpful error messages for invalid regex.

Tag conditions

Each tag condition checks against a single value. The value field does not support comma-separated lists -- if you enter tag1, tag2, tag3 as the value, it will be treated as one literal string, not three separate tags.

Without regex enabled, tag operators (equals, not equals, contains, not contains) compare the condition value against each of the torrent's tags individually.

  • equals / not equals: exact tag membership (case-insensitive)
  • contains / not contains: substring match per tag (case-insensitive)
Tag operators: contains is substring matching

Tags contains tag1 will also match torrents tagged tag10. For exact tag membership, prefer equals / not equals with one condition per tag and combine them with an OR group.

Matching any of multiple tags

To check whether a torrent has any one of several tags, create an OR group with one condition per tag (exact match):

#FieldOperatorValue
1Tagsequalstag1
2Tagsequalstag2
3Tagsequalstag3

Group these with OR logic so the rule matches when at least one tag is present.

To exclude torrents that have any of several tags, create an AND group of not equals conditions (exact match):

#FieldOperatorValue
1Tagsnot equalstag1
2Tagsnot equalstag2
3Tagsnot equalstag3

Group these with AND logic -- all three must be true, meaning none of those tags are present.

Using regex for tag matching

As an alternative to multiple conditions, you can use regex to match against several tags in a single condition. When regex is enabled, the pattern matches against the full raw tag string (e.g. cross-seed, noHL, racing) rather than checking each tag individually.

For example, to exclude torrents tagged with tag1 or tag2, use a single condition:

  • Field: Tags
  • Toggle: IF NOT (negate the match)
  • Operator: matches regex
  • Value: (^|,\s*)(tag1|tag2)(\s*,|$)

This evaluates the regex against the raw tags string. The delimiter-aware pattern ensures tag1 does not match tag10. The IF NOT toggle then negates the result, so the condition is true only for torrents that do not have either tag.

Live impact preview and dry-run

Workflow editing includes immediate feedback for delete/category workflows:

  • Live impact preview in the workflow dialog updates automatically as conditions/actions change.
  • Shows current impacted count plus a small preview list of matching torrents.
  • For category rules, the preview summary splits direct matches and cross-seed expansions.

You can also run dry-runs immediately without waiting for interval execution:

  • Workflow dialog: Dry-run now
  • Workflow list menu: Run dry-run now

Dry-run executes the current workflow config as a simulation only and writes results to automation activity.

No-match behavior:

  • Manual dry-runs still log a dry_run_no_match summary row when nothing matches.
  • Scheduled dry-run rules do not log no-match rows (to avoid event noise).

Tracker Matching

This is sort of not needed, since you can already scope trackers outside the workflows. But its available either way.

PatternExampleMatches
All*Every tracker
Exacttracker.example.comOnly that domain
Glob*.example.comSubdomains
Suffix.example.comDomain and subdomains

Separate multiple patterns with commas, semicolons, or pipes. All matching is case-insensitive.

Grouping

Grouping lets an automation treat "related torrents" as a single unit for:

  • Group-aware conditions: GROUP_SIZE, IS_GROUPED
  • Group expansion: apply an action to every torrent in the group (instead of only the matched torrent)
  • Strict matching: grouped expansion runs only when all members satisfy the action conditions

Group-Scoped Condition Fields

GROUP_SIZE and IS_GROUPED can be scoped per condition row:

  • Set groupId on each GROUP_SIZE / IS_GROUPED condition if you want explicit per-row grouping.
  • If a grouped condition row has no groupId, qui uses conditions.grouping.defaultGroupId.
  • If no default is configured, legacy unscoped grouped conditions fall back to cross_seed_content_save_path.

This allows multiple grouped conditions in the same workflow, each using different grouping strategies.

Action Expansion

Some actions accept a groupId. When set, qui expands the action to all torrents in that group.

Group expansion semantics are strict:

  • Every member in the expanded group must satisfy the action condition checks for that rule.
  • If any member fails (or the group cannot be resolved), qui skips the entire grouped action.
  • There is no "trigger-only fallback" when groupId is set.

Built-in group IDs:

  • cross_seed_content_path: same Content Path (normalized)
  • cross_seed_content_save_path: same Content Path + Save Path (normalized)
  • release_item: same Content Type + Effective Name
  • tracker_release_item: same Tracker + Content Type + Effective Name
  • hardlink_signature: same physical file set signature (requires local filesystem access)

Custom Groups (Advanced)

Rules can define custom groups via conditions.grouping.groups[] with:

  • id: string
  • keys: list of key names combined to form the group key

Supported keys:

  • contentPath, savePath, effectiveName, contentType, tracker
  • rlsSource, rlsResolution, rlsCodec, rlsHDR, rlsAudio, rlsChannels, rlsGroup
  • hardlinkSignature

For content-path-based grouping where Content Path == Save Path (ambiguous), you can set:

  • ambiguousPolicy: "verify_overlap" (default for cross-seed groups) with minFileOverlapPercent (default 90)
  • ambiguousPolicy: "skip"

Actions

Actions can be combined (except Delete which must be standalone). Each action supports an optional condition override.

Speed Limits

Set upload and/or download limits. Each field supports these modes:

ModeValueDescription
No change-Don't modify this field
Unlimited0Remove speed limit (qBittorrent treats 0 as unlimited)
Custom>0Specific limit in KiB/s or MiB/s

Applied in batches for efficiency.

Share Limits

Set ratio limit and/or seeding time limit. Each field supports these modes:

ModeValueDescription
No change-Don't modify this field
Use global-2Follow qBittorrent's global share settings
Unlimited-1No limit for this field
Custom>=0Specific value (ratio as decimal, time in minutes)

Torrents stop seeding when any enabled limit is reached.

Pause

Pause matching torrents. Only pauses if not already stopped.

If a resume action is also present, last action wins.

Resume

Resume matching torrents. Only resumes if not already running.

If a pause action is also present, last action wins.

Force Recheck

Force recheck matching torrents.

  • Triggers qBittorrent recheck for matched torrents.
  • Can be combined with other actions.
  • Supports optional condition override (like other actions).

Force Reannounce

Force reannounce matching torrents.

  • Triggers immediate tracker reannounce for matched torrents.
  • Can be combined with other actions.
  • Supports optional condition override (like other actions).

Delete

Remove torrents from qBittorrent. Must be standalone - cannot combine with other actions.

ModeDescription
deleteRemove from client, keep files
deleteWithFilesRemove with files
deleteWithFilesPreserveCrossSeedsRemove files but preserve if cross-seeds detected
deleteWithFilesIncludeCrossSeedsRemove files and also delete all cross-seeded torrents sharing the same files

Optional grouping (advanced):

Delete actions can specify a groupId to expand the deletion to all torrents in that group.

  • For delete (keep files): this is useful when you want "remove from client" to be cross-seed aware.
  • With groupId, keep-files delete is strict all-or-none: if any group member does not satisfy rule conditions, nothing in that group is removed.

Include cross-seeds mode behavior:

When a torrent matches the rule, the system finds other torrents that point to the same downloaded files (cross-seeds/duplicates) and deletes them together. This is useful when you want to fully remove content and all its cross-seeded copies at once.

  • Safe expansion: If qui can't safely confirm another torrent uses the same files, it won't be included in the deletion.
  • Safety-first: If verification can't complete for any reason, the entire group is skipped rather than risking broken torrents.
  • Preview: The delete preview shows all torrents that would be deleted, with cross-seeds marked.

Include hardlinked copies:

When "Include hardlinked copies" is enabled (only available with deleteWithFilesIncludeCrossSeeds mode), the system also deletes torrents that share the same underlying physical files via hardlinks, even if they have different Content Paths.

  • Requires: Local Filesystem Access must be enabled on the instance.
  • Safe scope: Only includes hardlinks that are fully contained within qBittorrent's torrent set. Never follows hardlinks to files outside qBittorrent (e.g., your media library).
  • Preview: Hardlink-expanded torrents are marked as "Cross-seed (hardlinked)" in the preview.
  • Free Space projection: When combined with Free Space conditions, hardlink groups are correctly deduplicated in the space projection - torrents sharing the same physical files are only counted once.

This is useful when you have hardlinked copies of content across different locations in qBittorrent and want to clean up all copies together.

Tag

Manage tags on torrents. You can add multiple Tag actions in one workflow.

ModeDescription
fullAdd to matches, remove from non-matches (smart toggle)
addOnly add to matches
removeOnly remove from matches
note

mode: remove removes tags from torrents that match the tag action condition. It does not remove from non-matches.

mode: full is evaluated within the rule's scope for that run (enabled rule, tracker pattern match, and run eligibility). It is not a client-wide sweep by itself.

Options:

  • Managed / Replace in Client - Managed (default) applies per-torrent add/remove diffs only. Replace in client deletes managed tags from qBittorrent first, then reapplies to current matches.
  • Use Tracker as Tag - Derive tag from tracker domain
  • Use Display Name - Use tracker customization display name instead of raw domain

Behavior reference:

ConfigurationBehavior
mode: full + ManagedAdds/removes tag for torrents this rule evaluates. No client-wide reset.
mode: full + Replace in clientDeletes selected tag(s) client-wide first, then re-adds only current matches.

If you see repeated activity like +tag=696 every run, that usually means Replace in client is enabled for that tag action.

Quick troubleshooting:

  1. Check logs for automations: deleted managed tags from client before retagging.
  2. In Automations UI, open enabled rules and verify whether "Replace in client" is enabled on any tag action.
  3. Confirm the activity entry's rule list matches the rule you expect.

Category

Move torrents to a different category.

Options:

  • Include Cross-Seeds - Also move cross-seeds (matching ContentPath AND SavePath)
  • Group ID (advanced) - Expand category changes to all torrents in the specified group (see Grouping). If set, this takes precedence over "Include Cross-Seeds".
  • Strict grouped matching - With groupId, category expansion applies only when all group members satisfy the category rule checks.
  • Block If Cross-Seed In Categories - Prevent move if another cross-seed is in protected categories

Move

Move torrents to a different path on disk. This is needed to move the contents if AutoTMM is not enabled.

Options:

  • Group ID (advanced) - Expand moves to all torrents in the specified group (see Grouping). The move path is resolved for the matched torrent and then applied to the whole group.
  • Strict grouped matching - With groupId, move expansion is all-or-none: every member must satisfy the move rule checks.
  • Skip if cross-seeds don't match the rule's conditions - Skip the move if the torrent has cross-seeds that don't match the rule's conditions. This is ignored when Group ID is set.

Move path templates

The move path is evaluated as a Go template for each torrent. You can use a fixed path (e.g. /data/archive) or template actions to build paths from torrent properties.

Available template variables:

VariableDescription
.NameTorrent display name
.HashInfo hash
.CategoryqBittorrent category
.IsolationFolderNameFilesystem-safe folder name (hash or sanitized name)
.TrackerTracker display name (when available from instance config), otherwise the tracker domain

Template function:

FunctionDescription
sanitizeMakes a string safe for use as a path segment (removes invalid characters). Use for user-controlled values like names, e.g. {{ sanitize .Name }}.

Examples:

  • Fixed path (no template actions): /data/archive
  • By category: /data/{{.Category}} → e.g. /data/movies
  • By name (safe for paths): /data/{{ sanitize .Name }}
  • By isolation folder: /data/{{.IsolationFolderName}}
  • By tracker: /data/{{.Tracker}} (when tracker display name is configured)

External Program

Run a pre-configured external program when torrents match the automation rule. Uses the same programs configured in Settings → External Programs.

FieldDescription
ProgramSelect from enabled external programs
Condition OverrideOptional condition specific to this action

Behavior:

  • Executes asynchronously (fire-and-forget) to avoid blocking automation processing
  • Can be combined with other actions (speed limits, share limits, pause, tag, category)
  • Only enabled programs appear in the dropdown
  • Activity is logged with rule name, torrent details, and success/failure status
note

The program must be enabled in Settings → External Programs to appear in the automation dropdown.

note

When multiple rules match the same torrent with External Program actions enabled, the last matching rule (by sort order) determines which program executes for that torrent. Only one program runs per torrent per automation cycle.

warning

The program's executable path must be present in the application's allowlist. Programs that are disabled or have forbidden paths will not run—attempts are rejected and logged in the activity log with the rule name and torrent details.

Use cases:

  • Run post-processing scripts when torrents complete
  • Notify external systems (webhooks, notifications) when conditions are met
  • Trigger media library scans after category changes
  • Execute cleanup scripts for old or stalled torrents

Cross-Seed Awareness

Automations detect cross-seeded torrents (same content/files) and can handle them specially:

  • Detection - Matches via ContentPath (and SavePath for category moves)
  • Delete Rules:
    • Use deleteWithFilesPreserveCrossSeeds to keep files if cross-seeds exist
    • Use deleteWithFilesIncludeCrossSeeds to delete matching torrents and all their cross-seeds together
  • Category Rules - Enable "Include Cross-Seeds" to move related torrents together
  • Blocking - Prevent category moves if cross-seeds are in protected categories

The HARDLINK_SCOPE field lets automations distinguish between torrents whose files are hardlinked into an external library (Sonarr, Radarr, etc.) and torrents that exist only within qBittorrent. This is the foundation for safe "Remove Upgraded Torrents" automations.

How scope is determined

When an automation references HARDLINK_SCOPE, qui builds a hardlink index by calling Lstat() on every file of every torrent in qBittorrent. For each file it extracts:

  • The inode and device ID — uniquely identifying the file on disk.
  • The nlink count — the total number of hardlinks to that inode, as reported by the filesystem.

It then counts how many unique file paths across the entire qBittorrent torrent set point to each inode. The scope for each torrent is determined by comparing these two numbers:

ScopeConditionMeaning
noneNo file has nlink > 1No hardlinks detected.
torrents_onlyAt least one file has nlink > 1, and no file has nlink > uniquePathCountHardlinks exist, but only between torrents in qBittorrent. No external library links.
outside_qbittorrentAny file has nlink > uniquePathCountSomething outside qBittorrent has hardlinked the file — typically a Sonarr/Radarr library import.
note

HARDLINK_SCOPE only reflects hardlink metadata. Cross-seeds are detected separately (ContentPath matching), so a torrent can have HARDLINK_SCOPE = none and still be cross-seeded.

Unknown scope and safety behavior

If qui cannot Lstat() any file in a torrent — due to wrong paths, missing permissions, or inaccessible storage — that torrent receives no scope entry. All HARDLINK_SCOPE conditions evaluate to false for that torrent, regardless of the operator or value. This is a safety measure to prevent unintended deletions of torrents qui cannot fully inspect.

To diagnose this, enable debug logging and look for the "hardlink index built" log message, which reports an inaccessible count.

Docker volume requirements

For hardlink scope detection to work in Docker:

  1. Paths must match exactly. qui must be able to read files at the same paths qBittorrent reports. If qBittorrent says a torrent's save path is /data/torrents/radarr/, qui must be able to access /data/torrents/radarr/ inside its container.

  2. Same underlying storage. Both containers must share the same host mount so that inode numbers are consistent. If qui and qBittorrent access the same files through different host mounts or different bind-mount configurations, inode numbers may not match.

  3. Single mount, not subdivided. Mount the common parent directory rather than mounting subdirectories separately. For example, if your data lives under /mnt/media/data on the host:

services:
qui:
volumes:
- /home/user/docker/qui:/config
- /mnt/media/data:/data # single mount covering both torrents and library

Avoid mounting both /mnt/media/data/torrents:/data/torrents and /mnt/media/data:/data — the overlapping mounts can cause inconsistent inode visibility. Use a single mount at the common parent.

Filesystem limitations

Hardlink scope detection depends on the kernel reporting accurate nlink values in stat results. Some filesystems do not do this:

  • FUSE-based filesystems (sshfs, mergerfs, rclone mount) may report nlink = 1 for all files regardless of actual hardlink count.
  • Some NAS appliance filesystems and overlay filesystems (overlayfs) may behave similarly.
  • Network filesystems (NFS, CIFS/SMB) generally report accurate nlink values but behavior varies by server implementation.

On affected filesystems, every torrent appears to have scope none because nlink is always 1. There is no workaround within qui — this is a kernel/filesystem limitation. If you suspect this issue, run stat on a file you know is hardlinked and check the "Links" count.

Hardlinks also cannot span across different filesystems. If your torrent data and media library are on separate filesystems (or separate Docker volumes backed by different host paths), Sonarr/Radarr will copy instead of hardlink, and scope detection has nothing to detect.

Example: Remove Upgraded Torrents

This automation deletes torrents that have been replaced by an upgrade in Sonarr/Radarr. It targets torrents where the library hardlink no longer exists (the arr removed or re-linked it during upgrade), the torrent has been seeding for at least 7 days, and the category matches your arr categories.

tip

Use HARDLINK_SCOPE with NOT_EQUAL to outside_qbittorrent rather than EQUAL to none. This way torrents with scope torrents_only (cross-seeded but not in a library) are also eligible for cleanup, while any torrent still linked into your media library is protected.

{
"name": "Remove Upgraded Torrents",
"trackerPattern": "*",
"trackerDomains": ["*"],
"conditions": {
"schemaVersion": "1",
"delete": {
"enabled": true,
"mode": "deleteWithFilesPreserveCrossSeeds",
"condition": {
"operator": "AND",
"conditions": [
{
"operator": "OR",
"conditions": [
{ "field": "CATEGORY", "operator": "EQUAL", "value": "radarr" },
{
"field": "CATEGORY",
"operator": "EQUAL",
"value": "radarr.cross"
},
{
"field": "CATEGORY",
"operator": "EQUAL",
"value": "tv-sonarr"
},
{
"field": "CATEGORY",
"operator": "EQUAL",
"value": "tv-sonarr.cross"
}
]
},
{
"field": "HARDLINK_SCOPE",
"operator": "NOT_EQUAL",
"value": "outside_qbittorrent"
},
{
"field": "SEEDING_TIME",
"operator": "GREATER_THAN_OR_EQUAL",
"value": "604800"
}
]
}
}
}
}

This works because when Sonarr/Radarr upgrades a release, the old library hardlink is removed. The old torrent's files then have nlink == 1 (scope none) or are only linked to other torrents (scope torrents_only). Either way, the scope is not outside_qbittorrent, so the automation matches and deletes the torrent after the seeding time requirement is met.

If the automation is matching torrents you expect to be protected, verify:

  1. qui can access all torrent files at the paths qBittorrent reports (check debug logs for inaccessible files).
  2. Your filesystem reports accurate nlink values (stat <file> should show Links > 1 for hardlinked files).
  3. Your Docker volume mounts do not overlap or subdivide the storage in a way that breaks inode consistency.

Missing Files Detection

The Has Missing Files field detects whether any files belonging to a completed torrent are missing from disk.

  • Only checks completed torrents
  • Returns true if any file is missing from its expected path
note

Requires "Local filesystem access" enabled on the instance.

Important Behavior

Settings Only Set Values

Automations apply settings but do not revert when disabled or deleted. If a rule sets upload limit to 1000 KiB/s, affected torrents keep that limit until manually changed or another rule applies a different value.

Efficient Updates

Only sends API calls when the torrent's current setting differs from the desired value. No-op updates are skipped.

Processing Order

  • First match wins for delete actions (delete ends torrent processing, no further rules evaluated)
  • Last rule wins for speed limits, share limits, category, and external program actions
  • Accumulative for tag actions (tags are combined across matching rules)

Free Space Condition Behavior

When using the Free Space condition in delete rules, the system uses intelligent cumulative tracking:

  1. Oldest-first processing - Torrents are sorted by age (oldest first) for deterministic, predictable cleanup
  2. Cumulative space tracking - As each torrent is marked for deletion, its size is added to the projected free space (only when the delete mode actually frees disk bytes)
  3. Stop when satisfied - Once Free Space + Space To Be Cleared exceeds your threshold, remaining torrents no longer match
  4. Cross-seed aware - Cross-seeded torrents sharing the same files are only counted once to avoid overestimating freed space

Preview Views for Free Space Rules

When previewing a delete rule with a Free Space condition, a toggle allows switching between two views:

ViewDescription
Needed to reach targetShows only the torrents that would be removed right now to reach your free-space target. This is the default view and reflects actual delete behavior.
All eligibleShows all torrents this rule could remove while free space is low. Useful for understanding the full scope of what the rule could potentially delete (may include cross-seeds that don't directly match filters).

The toggle only appears for delete rules that use the Free Space condition.

Preview features:

  • Path column - Shows the content path for each torrent with copy-to-clipboard support
  • Export CSV - Download the full preview list (all pages) as a CSV file for external analysis

Cross-seed expansion in previews:

Cross-seeds are only expanded and displayed in the preview when using Remove with files (include cross-seeds) mode. In this mode, the preview shows all torrents that would be deleted together, with cross-seeds clearly marked. Other delete modes don't expand cross-seeds in the preview since they either preserve cross-seeds or don't consider them specially.

Delete mode affects space projection:

Delete ModeSpace Added to Projection
Remove with filesFull torrent size
Preserve cross-seeds (no cross-seeds)Full torrent size
Preserve cross-seeds (has cross-seeds)0 (files kept)

How preserve cross-seeds works:

  • Cross-seed detection checks if any other torrent shares the same Content Path at evaluation time (before any removals).
  • If multiple torrents share the same files, removing them all in one rule run will still keep the files on disk. No disk space is freed from that group because each torrent sees the others as cross-seeds.
  • Only non-cross-seeded torrents contribute to the free-space projection when using preserve mode.

Example: With 400GB free and a rule "Delete if Free Space < 500GB" using Remove with files, the system deletes oldest torrents until the cumulative freed space reaches 100GB, then stops. A 50GB torrent and its cross-seed (same files) only count as 50GB freed, not 100GB.

note

The UI and API prevent combining Remove (keep files) mode with Free Space conditions. Since keep-files doesn't free disk space, such a rule could never satisfy the free space target and would match indefinitely.

note

After removing files, qui waits ~5 minutes before running Free Space deletes again to allow qBittorrent to refresh its disk free space reading. The UI prevents selecting 1 minute intervals for Free Space delete rules.

Free Space Source

By default, Free Space uses qBittorrent's reported free space (based on its default download location). If you have multiple disks or want to manage a specific mount point, select "Path on server" and enter the path to that disk.

SourceDescription
Default (qBittorrent)Uses qBittorrent's reported free space
Path on serverReads free space from a specific filesystem path
note

Path on server requires "Local Filesystem Access" to be enabled on the instance.

If you want to manage multiple disks, create one workflow per disk and set a different Path on server for each workflow.

note

On Windows, Path on server is not supported and Free Space always uses qBittorrent's reported free space. The UI disables the option and switches legacy workflows back to the default when opened.

Batching

Torrents are grouped by action value and sent to qBittorrent in batches of up to 50 hashes per API call.

Activity Log

All automation actions are logged with:

  • Torrent name and hash
  • Rule name and action type
  • Outcome (success/failed) with reasons
  • Action-specific details

Activity is retained for 7 days by default. View the log in the Automations section for each instance.

Example Rules

Delete Old Completed Torrents

Remove torrents completed over 30 days ago when disk space is low:

  • Condition: Completion On Age > 30 days AND State is completed AND Free Space < 500GB
  • Action: Remove with files

Deletes oldest matching torrents first, stopping once enough space would be freed to exceed 500GB.

Speed Limit Private Trackers

Limit upload on private trackers:

  • Tracker: *
  • Condition: Private is true
  • Action: Upload limit 10000 KiB/s

Tag Stalled Torrents

Auto-tag torrents with no activity:

  • Tracker: *
  • Condition: Last Activity Age > 7 days
  • Action: Tag "stalled" (mode: add)

Clean Unregistered Torrents

Remove torrents the tracker no longer recognizes:

  • Tracker: *
  • Condition: Is Unregistered is true
  • Action: Delete (keep files)

Maintain Minimum Free Space

Keep at least 200GB free by removing oldest completed torrents:

  • Tracker: *
  • Condition: Free Space < 200GB AND State is completed
  • Action: Remove with files (preserve cross-seeds)

Removes torrents from the client, oldest first, until enough space is projected to be freed. Cross-seeded torrents keep their files on disk and don't contribute to the projection. If only cross-seeded torrents match, this may remove many torrents without freeing any disk space.

Clean Up Old Content with Cross-Seeds

Remove completed torrents and all their cross-seeded copies when they're old enough:

  • Tracker: *
  • Condition: Completion On Age > 30 days AND State is completed
  • Action: Remove with files (include cross-seeds)

When a torrent matches, any other torrents pointing to the same downloaded files are deleted together. Useful for complete cleanup when you no longer need any copy of the content.

Organize by Tracker

Move torrents to tracker-named categories:

  • Tracker: tracker.example.com
  • Action: Category "example" with "Include Cross-Seeds" enabled

Post-Processing on Completion

Run a script when torrents finish downloading:

  • Tracker: *
  • Condition: State is completed AND Progress = 100
  • Action: External Program "post-process.sh"

Notify on Stalled Torrents

Alert an external monitoring system when torrents stall:

  • Tracker: *
  • Condition: State is stalled AND Last Activity Age > 24 hours
  • Action: External Program "send-alert" + Tag "stalled" (mode: add)