Skip to content

Configuration

jack reads a JSONC file (comments allowed) from APP_CONFIG_PATH (default /config/config.jsonc). If the file doesn't exist, jack writes a default one on first boot. Copy examples/config.jsonc as a starting point.

The jack block is required. downloads, servers, and peers are optional, so configure only what you need for what you're doing. (A top-level version number also appears in the file; jack manages it for config migrations, so leave it alone.)

Quick reference

KeyWhat it does
jack.internalUrlURL your *arr apps use to reach jack
jack.tmdbApiKeyTMDB metadata for the catalog
jack.external.instanceNamePeer name suggested in your quick links
jack.external.urlURL peers use to reach you
jack.external.headersHeaders peers must send to reach you
downloads.completedPathWhere finished downloads go
downloads.maxConcurrentDownloadsSimultaneous transfer cap
downloads.maxDownloadAttemptsRetries before a download fails
downloads.retryBaseDelayMsDownload retry backoff base
downloads.retryMaxDelayMsDownload retry backoff cap
downloads.idleTimeoutMsStalled-transfer timeout
downloads.importPollIntervalMsImport watcher poll cadence
downloads.maxManualImportAttemptsRetries before an import fails
downloads.manualImportBackoffBaseMsImport retry backoff base
downloads.manualImportBackoffMaxMsImport retry backoff cap
downloads.unlinkImportedFilesDrop jack's copy after *arr imports
servers[].nameDisplay name
servers[].typeRadarr or Sonarr
servers[].url*arr base URL
servers[].apiKey*arr API key
servers[].headersExtra outbound headers
servers[].sourceShare this library with peers
servers[].destinationSearch + import through this *arr
servers[].autoregisterIndexer/client registration control
peers[].nameDisplay name
peers[].urlFriend's peer URL
peers[].apiKeyKey the friend issued you
peers[].headersExtra outbound headers

jack

This instance's identity. Required.

jack.internalUrl

Type: string · Required
Format: URL

URL your own *arr apps use to reach jack, and the address jack registers for its Torznab indexer and qBittorrent download client. Must be resolvable from the Radarr/Sonarr side: on a shared Docker network use the container name (http://jack:5225); otherwise the host IP/domain. Peers never use this URL. They use whatever you hand them (see API keys & peering).

jack.tmdbApiKey

Type: ConfigSecret

TMDB v3 API key, used by the management UI's catalog to enrich peer libraries with artwork and metadata.

jack.external

Type: object

How another jack reaches this instance, and the profile jack encodes into the quick links you generate. Optional: without it, peering still works, you just hand out the URL and key by hand.

Edit it from the management UI (Settings -> Quick linking), which writes this block through its own endpoints so the rest of jack is left untouched.

jack.external.instanceName

Type: string
Format: 1–100 characters

The peer name suggested to whoever imports one of your quick links. They can change it before saving the peer. The management UI won't generate a link until this is set; calling the API directly, the suggested name comes from the request body instead.

jack.external.url

Type: string · Required
Format: http/https URL, no embedded user:password@ credentials

The URL a peer should use to reach this instance, and what ends up in the url field of every quick link you generate. Distinct from jack.internalUrl, which is for your own *arr apps. Use https://: the peer's API key travels in a request header.

jack.external.headers

Type: object
Content: header name -> ConfigSecret value
Default: {}

Extra headers a peer must send to get through whatever sits in front of you, such as Cloudflare Access service tokens. They're copied into the quick link so your friend doesn't have to configure them by hand, landing in their config as peers[].headers.

Values are resolved when the profile is saved and when a link is generated, while env and file references remain references in your config file. Reserved headers (X-Api-Key, Host, Content-Length, Connection, Transfer-Encoding), duplicate names, and values with line breaks are rejected; at most 100 headers.

downloads

Needed to consume (download) from peers, since jack only registers itself as a qBittorrent download client when this block is present. Everything except completedPath is an optional tuning knob with a sensible default.

Every key here is also editable from the management UI (Settings -> Downloads). Clearing a field there removes the key from the file, so the default below applies again. Only unlinkImportedFiles takes effect immediately; the rest are read at startup, so restart jack after changing them.

downloads.completedPath

Type: string · Required
Format: non-empty filesystem path (inside jack's container)

Where jack writes finished downloads. The path is inside jack's container; jack creates it if missing. It must also be mounted into your Radarr and Sonarr containers at the same path, because *arr resolves it in its own filesystem to import finished files (see the callout in Getting started).

downloads.maxConcurrentDownloads

Type: integer
Format: ≥ 1
Default: 3

Maximum simultaneous transfers from peers.

downloads.maxDownloadAttempts

Type: integer
Format: ≥ 1
Default: 13

How many times a failing download is attempted before it's marked failed.

downloads.retryBaseDelayMs

Type: integer
Format: milliseconds, ≥ 0
Default: 1000

Base delay for the exponential backoff between download retries.

downloads.retryMaxDelayMs

Type: integer
Format: milliseconds, ≥ 0
Default: 1800000

Upper bound for the download retry backoff. The default is 30 minutes.

downloads.idleTimeoutMs

Type: integer
Format: milliseconds, ≥ 1000
Default: 60000

How long a transfer may go without receiving data before it's considered stalled and retried.

downloads.importPollIntervalMs

Type: integer
Format: milliseconds, ≥ 1000
Default: 30000

How often the import watcher polls each destination *arr's history to detect which finished downloads were imported.

downloads.maxManualImportAttempts

Type: integer
Format: ≥ 1
Default: 6

When a manual import trigger keeps failing (e.g. *arr returns 500 because the title's library folder is missing), jack backs off between attempts and marks the download failed after this many attempts.

downloads.manualImportBackoffBaseMs

Type: integer
Format: milliseconds, ≥ 0
Default: 60000

Base delay for the manual-import retry backoff.

downloads.manualImportBackoffMaxMs

Type: integer
Format: milliseconds, ≥ 0
Default: 1800000

Upper bound for the manual-import retry backoff. The default is 30 minutes.

downloads.unlinkImportedFiles

Type: boolean
Default: false

Remove jack's copy of a download from completedPath once the *arr that grabbed it confirms the import. jack has no use for the file after that, since it is never re-served or re-imported.

jack calls unlink on that one file and nothing else, so what actually happens depends on how your *arr imports:

  • Hardlink (Radarr/Sonarr's default when the download and library folders share a filesystem): the library's link keeps the data alive, and only jack's extra directory entry disappears.
  • Copy or move: the library already has its own bytes, so removing jack's copy just frees the space.

The unlink only ever runs on an import jack has confirmed: either the destination *arr reports the download in its import history, or the manual import command jack pushed reports completed. Downloads that are queued, still importing, or failed keep their file, and a file that another download row still references is left alone. If the unlink fails, the download stays imported and the failure is logged.

Editable from the management UI (Settings -> Downloads) and applies immediately. Unlike the other keys in this block, it does not need a restart.

servers

Your Radarr/Sonarr servers. Each entry can be a source, a destination, or both (see Concepts).

servers[].name

Type: string · Required

Display name, used in logs, health output, and search results.

servers[].type

Type: "radarr" | "sonarr" · Required

Which *arr this server is.

servers[].url

Type: string · Required
Format: URL

Base URL of the *arr server, reachable from jack.

servers[].apiKey

Type: ConfigSecret · Required
Format: resolves to exactly 32 hexadecimal characters

The Radarr/Sonarr API key (Settings -> General).

servers[].headers

Type: object
Content: header name -> ConfigSecret value
Default: {}

Extra HTTP headers sent to this server, for reverse proxies or access layers such as Cloudflare Access or Authelia. These are outbound connector headers only; jack still adds the required *arr X-Api-Key auth header separately.

servers[].source

Type: boolean
Default: true

Share this library with peers.

servers[].destination

Type: boolean
Default: true

Register jack in this server as an indexer + download client and import grabs into it.

servers[].autoregister

Type: object
Content: enable: boolean, default true · priority: integer ≥ 1, default 1

Controls the indexer/download-client registration jack performs in destination servers on startup.

  • enable: set false to register jack in that *arr yourself.
  • priority: indexer priority in *arr (lower = preferred). The qBittorrent download client is always registered at *arr's lowest priority (50): *arr's general client pool only round-robins among the best-priority group, so torrents grabbed from your other indexers never get routed to jack's client. Grabs from the Jack indexer still reach it because the indexer is bound to the client explicitly (downloadClientId), which *arr resolves before applying priority.

peers

Other jack instances (friends) you consume from. Sources only; sharing back is configured on their side.

peers[].name

Type: string · Required

Display name, used in logs, health output, and search results.

peers[].url

Type: string · Required
Format: URL

The reachable peer URL your friend gave you. Use https://, since the API key travels in a request header, and jack logs a startup warning for http:// peers.

peers[].apiKey

Type: ConfigSecret · Required

The peer API key that friend issued you (see API keys & peering), not your own.

peers[].headers

Type: object
Content: header name -> ConfigSecret value
Default: {}

Extra HTTP headers sent to this peer, with the same semantics as servers[].headers, e.g. Cloudflare Access service tokens.

ConfigSecret

Every secret-valued key shares the ConfigSecret type: a value that can be given as a plain string, as a reference to an environment variable, or as a reference to a secret file, so secrets can stay out of the config file:

jsonc
{
  // plain string
  "apiKey": "plain-string"
}
jsonc
{
  // environment variable reference
  "apiKey": { "env": "RADARR_API_KEY" }
}
jsonc
{
  // secret file reference
  "apiKey": { "file": "/run/secrets/radarr_api_key" }
}

All three forms are interchangeable everywhere a secret appears (jack, servers, peers, and headers values). File paths must be absolute; trailing line endings are ignored. If a referenced variable is unset/empty, or a secret file cannot be read or resolves to an empty value at startup, jack reports the problem and refuses to load that config.

Full example

jsonc
{
  "jack": {
    "internalUrl": "http://jack:5225",
    "tmdbApiKey": { "env": "TMDB_API_KEY" },
    "external": {
      "instanceName": "Roz's Jack",
      "url": "https://jack.example.com",
      "headers": {
        "CF-Access-Client-Id": { "env": "MY_CF_CLIENT_ID" },
        "CF-Access-Client-Secret": { "env": "MY_CF_CLIENT_SECRET" }
      }
    }
  },
  "downloads": {
    "completedPath": "/data/torrents/completed",
    "maxConcurrentDownloads": 3
  },
  "servers": [
    {
      "name": "Main Radarr",
      "type": "radarr",
      "url": "http://radarr:7878",
      "apiKey": { "env": "RADARR_API_KEY" },
      "source": true,
      "destination": true,
      "autoregister": {
        "enable": true,
        "priority": 1
      }
    },
    {
      "name": "Main Sonarr",
      "type": "sonarr",
      "url": "http://sonarr:8989",
      "apiKey": { "env": "SONARR_API_KEY" }
    }
  ],
  "peers": [
    {
      "name": "friend",
      "url": "https://their-jack.example.com",
      "apiKey": { "env": "FRIEND_JACK_API_KEY" },
      "headers": {
        "CF-Access-Client-Id": { "env": "FRIEND_CF_CLIENT_ID" },
        "CF-Access-Client-Secret": { "env": "FRIEND_CF_CLIENT_SECRET" }
      }
    }
  ]
}

Released under the GPL-3.0 License.