Skip to content

Label Sync

khak1s edited this page May 3, 2026 · 2 revisions

Label Sync Guide

Label Sync mirrors a tag from one service onto a tag or label in another. Pick a source service and tag (e.g., a "kids" tag in Sonarr), pick a destination service and tag (e.g., a "kids" label in Plex), and Label Sync keeps the destination in sync. Works any-to-any across Sonarr, Radarr, Plex, Jellyfin, and Emby — including same-service rules.

Overview

A Label Sync rule is a directed mapping:

[Source]                    [Destination]
  Sonarr "kids" tag    ──→    Plex "kids" label
  Plex "favorites"     ──→    Sonarr "favorites" tag
  Sonarr (instance A)  ──→    Sonarr (instance B)

The workflow is:

  1. Configure — Define a rule with source service + source tag and destination service + destination instance + destination tag
  2. Match — On a 5-minute scheduler tick (with a 60-minute per-rule cooldown), Label Sync reads tagged items from the source and resolves them to items on the destination
  3. Apply — The destination tag/label is created if absent and merged additively onto matched items

Label Sync is the propagation half of the auto-tag/sync pipeline. Pair it with Auto-Tagger if you want criteria-based tagging at the source plus mirroring to other services.


Getting Started

Prerequisites

You need at least one source instance and one destination instance configured in Settings:

  • Sonarr / Radarr — for *arr-side tags
  • Plex / Jellyfin / Emby — for media-server-side labels

A rule can use any service as either source or destination, including same-service rules (e.g., Sonarr → Sonarr to mirror tags between two Sonarr instances).

Access Label Sync

  1. Log in to Arr Dashboard
  2. Click Label Sync in the sidebar (Maintenance section)
  3. The page lists your existing rules with last-run status and a "Run now" button per rule

How It Works

Source and Destination

Each rule declares:

Field Description
Source Service One of sonarr, radarr, plex, jellyfin, emby
Source Instance A specific instance, or "all instances of this service"
Source Tag Name The tag/label to read from the source
Destination Service One of sonarr, radarr, plex, jellyfin, emby
Destination Instance A specific instance (always required)
Destination Tag Name The tag/label to apply on the destination (created if absent)

The destination is always a single specific instance. The source can be a single instance or all enabled instances of a service — the latter is convenient for users running multiple Sonarrs (e.g., 1080p + 4K) who want both to feed a single Plex instance.

Cross-Service Identity Matching

When source and destination are different services, Label Sync needs to match items across systems. It does this via TMDb ID, populated during the regular library/cache sync. An item without a TMDb ID on either side cannot be cross-mapped, so it will not receive the destination tag.

For same-service rules (e.g., Sonarr → Sonarr or Plex → Plex), the matching uses the service's native identity (item ID).

Additive Merge

Tags are always merged into the destination's existing tag list — never replaced. If an item already has the destination tag, the rule is a no-op for that item.

What Label Sync Does NOT Do

  • No removal. If a source item loses its tag later, Label Sync does not remove the destination tag. This is a deliberate v1 limitation; bidirectional state tracking would change the rule's surface significantly.
  • No conflict resolution. Multiple rules writing different destination tags to the same item will all apply additively. There is no "last writer wins" mode.
  • No filtering. Unlike Auto-Tagger, Label Sync has no criteria DSL — it propagates an existing tag verbatim. If you need filtering (e.g., "only sync the kids tag for items also tagged 'family'"), use Auto-Tagger to seed a more specific source tag and sync that.

Triggers

Label Sync rules fire on four trigger sources, each with different latency characteristics. The fastest trigger that applies wins; the slower ones serve as safety nets if a faster one missed.

Trigger Fires when Latency
Auto-tagger chain The arr-dashboard auto-tagger writes a tag matched by a Label Sync rule's source service + tag name Sub-second when the Connect webhook is configured on the Sonarr/Radarr instance; up to ~5 min via the auto-tagger scheduler otherwise
Library-sync delta detection A tag was added or removed on an item externally (Sonarr/Radarr UI, third-party tool, manual edit) and the next library-sync poll detects the diff 5–15 min (matches your library-sync interval)
Per-item "Sync labels now" button User clicks the button on the library item detail modal Instant
Scheduler safety net Hourly tick runs all enabled rules end-to-end ≤1 hour

All four triggers funnel through the same executor and apply the same idempotency rules — already-applied labels no-op at the writer layer, so triggering the same rule for the same item multiple times is safe and inexpensive.

*Currently arr-source rules only. The auto-tagger chain and library-sync delta detection only fire for rules where the source service is Sonarr or Radarr. Rules with Plex / Jellyfin / Emby as the source still rely on the per-item button or the hourly scheduler. A future release will extend delta detection to media-server source labels.

Plex must already have the item. A trigger fired for a brand-new item only succeeds if Plex has already scanned the file and matched it by tmdbId. If the trigger fires before Plex catches up, the executor returns "0 matches" silently — the next trigger (or the hourly scheduler) heals the gap once Plex completes its scan.

Eventual consistency

The four triggers compose so that every realistic tag change converges, even if individual triggers miss:

  • Best case (auto-grab + Connect webhook): Radarr imports → auto-tagger applies tag → Label Sync fires inline → Plex label visible in seconds
  • *Common case (manual arr edit): User tags in Sonarr UI → next library-sync poll detects the diff → Label Sync fires → label visible within ~10 min
  • Edge case: Trigger fires before Plex has scanned the new file → 0 matches → scheduler tick within the next hour heals it once Plex item exists

In every path the system converges. No path leaves a state permanently incorrect.

Concurrency

The hourly scheduler's two ticks cannot overlap (a singleton in-flight guard skips the second). Event-driven triggers are independent and can run concurrently — the executor is idempotent at the writer layer, so concurrent runs for the same rule + item produce the same final state.


Configuration

Creating a Rule

  1. Click Add Rule on the Label Sync page
  2. Give the rule a Name (shown in the rules list and scheduler logs)
  3. Pick Source Service from the dropdown (Sonarr, Radarr, Plex, Jellyfin, Emby)
  4. Pick Source Instance — either a specific instance, or "all instances" of the source service
  5. Enter the Source Tag Name — the tag/label name on the source side (case-sensitive)
  6. Pick Destination Service from the dropdown
  7. Pick Destination Instance — always a specific instance (required)
  8. Enter the Destination Tag Name — the tag/label to write. Created automatically on the destination if it does not exist.
  9. Toggle Enabled — disabled rules are skipped by the scheduler
  10. Save the rule

Run Now

Each rule has a Run now button that executes the rule immediately and persists the result (lastRunAt, lastRunStatus, lastRunMessage). This runs the rule against the full source library; use it after creating a new rule to backfill labels for items that already carry the source tag.

"Sync labels now" — per-item manual trigger

Each Sonarr/Radarr item's detail modal (Library page → click an item) includes a Sync labels now button. Clicking it fires every enabled Label Sync rule whose source matches that item's *arr instance, scoped to just that one item.

Use cases:

  • You just changed a tag in Sonarr/Radarr's UI and want to verify Plex propagation without waiting for the next library-sync tick
  • You added a new Label Sync rule and want to test it on one specific item before letting it apply library-wide
  • You have a Plex-source rule (no automatic event-driven trigger) and want to push the label now

The button surfaces the result inline ("Synced 1 label", "No matching rules configured", or an error message). Currently movie and series items only — per-episode and music items are deferred to a future release.


Composition with Auto-Tagger

Label Sync propagates tags. Auto-Tagger creates tags. Together they form a two-stage pipeline:

[Auto-Tagger]                       [Label Sync]
  Genre = "Anime" rule    ──→        Sonarr "Anime" tag    ──→        Plex "Anime" label
  CF Score > 1000 rule    ──→        Radarr "Premium" tag  ──→        Jellyfin "Premium" label

This split keeps each engine focused. Auto-Tagger handles "when should this tag exist?" (criteria-based). Label Sync handles "where else should this tag live?" (propagation).

You can also use Label Sync standalone if you tag items manually in Sonarr/Radarr/Plex and want those tags mirrored elsewhere.


Operations

Status and Telemetry

Each rule row on the Label Sync page shows:

Field Meaning
Last Run At Timestamp of the last execution (scheduled or on-demand)
Last Run Status success (all items propagated), partial (some items failed), or failed (rule errored before any propagation)
Last Run Message A breakdown like "Scanned 2 source instances, found 47 tagged items, matched 42 on destination, applied 38 labels (4 already had it)" or a failure reason

Internal Run Totals

The executor tracks five counts per run, exposed in the run message:

Counter Meaning
sourceInstancesScanned How many source instances were walked
taggedItemsFound Total source items carrying the source tag
destMatchesFound Source items that resolved to a destination item
labelsApplied Items where the destination tag was actually written (excludes items that already had it)
failures Items that errored during the destination write

A partial status means failures > 0 but at least one application succeeded. A failed status means the entire run errored before any application.


Best Practices

Use Stable Tag Names

Source and destination tag names are stored verbatim. Renaming a tag on the source means the rule stops matching until you update the rule (or rename the source tag back). Pick names you intend to keep.

Pair with Auto-Tagger for Criteria-Driven Workflows

If you find yourself manually tagging items in Sonarr/Radarr just so Label Sync can propagate the tag, Auto-Tagger is the better tool. Define the criteria once, let Auto-Tagger maintain the source tag, let Label Sync handle the rest.

Verify Library Cache Coverage Before Going Wide

Cross-service rules depend on TMDb IDs being populated in both source and destination caches. If either cache is missing TMDb IDs, items cannot be cross-mapped. Check Settings → Services for cache health before assuming a rule is broken.

Treat the Destination as Append-Only

Because Label Sync never removes a tag, the destination accumulates labels over time. This is fine for tags representing fixed truths (e.g., "anime", "kids") but less ideal for transient state ("currently watching"). Use Run now after deleting a destination tag to re-establish a clean baseline.


Troubleshooting

"Rule shows 0 labels applied even though source items have the tag"

Causes:

  • The source tag name is misspelled or has different casing (tags are case-sensitive on most services)
  • TMDb IDs are missing on either side, so cross-service matching fails
  • The destination instance does not contain matching items

Solutions:

  1. Confirm the source tag name matches exactly in the source service (open Settings → Tags in Sonarr/Radarr or the labels list in Plex)
  2. Check Settings → Services and refresh the library cache for both source and destination
  3. Verify the destination has the items you expect (a Plex library that doesn't include the source content cannot match)

"Same-service rule (e.g., Sonarr → Sonarr) matches everything"

Cause: Same-service rules use native item IDs, not TMDb IDs. If both instances reference the same content but with different IDs (e.g., separate Sonarr DBs), the rule will not match correctly.

Solution: Same-service rules work best when both instances share the same underlying content database, or when both share TMDb IDs that can be used as the cross-key. Verify with one or two test items via "Run now" before relying on the rule.

"Destination tag not appearing in Plex/Jellyfin/Emby"

Causes:

  • The Plex/Jellyfin/Emby instance's user account does not have permission to write labels
  • The tag was applied but the media server's UI is showing cached data

Solutions:

  1. Confirm the API token configured for the destination instance has write access
  2. Refresh metadata for the affected library in the destination service
  3. Check the dashboard's Label Sync run message for labelsApplied count; if non-zero, the write did happen on the API side

"Rule shows last_run_status: failed"

Cause: The executor errored before any item was processed — typically an unreachable instance, an unsupported service in the URL, or an authentication failure.

Solutions:

  1. Read the lastRunMessage for the specific reason
  2. Check Settings → Services for connectivity to both source and destination
  3. Verify both instances are enabled

"I want to remove a destination tag when the source tag is removed"

Status: Not supported in v1. Label Sync is additive-only. To remove a destination tag, delete it manually from the destination service.

This is a deliberate v1 limitation — bidirectional sync semantics would require change-tracking infrastructure that is out of scope for the initial release.


API Reference

All endpoints require a session cookie.

List Rules

GET /api/label-sync/rules

Returns the user's rules sorted by createdAt descending.

Create Rule

POST /api/label-sync/rules
Content-Type: application/json

{
  "name": "Sonarr kids → Plex kids",
  "enabled": true,
  "sourceService": "sonarr",
  "sourceInstanceId": null,
  "sourceTagName": "kids",
  "destService": "plex",
  "destInstanceId": "<plex-instance-uuid>",
  "destTagName": "kids"
}

sourceInstanceId: null means "all enabled Sonarr instances owned by this user." destInstanceId is always a specific instance ID.

Both source and destination instances are validated for ownership and enablement at create time. Returns 404 if either is missing, disabled, or owned by a different user.

Update Rule

PATCH /api/label-sync/rules/:id
Content-Type: application/json

{ "enabled": false }

Any subset of fields can be updated. Source/destination changes are re-validated against the user's instances.

Delete Rule

DELETE /api/label-sync/rules/:id

Run Rule On Demand

POST /api/label-sync/rules/:id/run

Executes the rule immediately and persists lastRunAt, lastRunStatus, lastRunMessage. Disabled rules return HTTP 400 — enable the rule first.

Response shape:

{
  "rule": {
    "id": "...",
    "name": "Sonarr kids → Plex kids",
    "lastRunAt": "2026-05-01T12:34:56Z",
    "lastRunStatus": "success",
    "lastRunMessage": "Scanned 2 source instances, found 47 tagged items, matched 42 on destination, applied 38 labels.",
    ...
  }
}

Clone this wiki locally