Skip to content

Docker and environment

See the quick start for the compose one-liner. The default command is serve - a persistent GUI, not a one-shot job. Everything Coldarr persists (coldarr.yaml, the encrypted connection store + its key, move history) lives under one bind-mounted /config directory. To run one-shot CLI commands (or a cron job) against that same volume instead of the GUI, see the CLI guide.

Environment variables

The image understands these variables directly. Everything else (tiers, tags, thresholds) goes through coldarr.yaml, which supports ${VAR} substitution against whatever environment variables you pass to the container.

Container

PUID / PGID
The uid/gid the process runs as, so it can read your bind mounts. Same convention as Radarr/Sonarr/Jellyfin images. Default 1000/1000. Ignored if the container is already started as non-root (docker run --user, compose user:, a rootless runtime, or a Kubernetes securityContext) - the entrypoint skips straight to running as whatever user it was given.
TZ
Container timezone, mostly cosmetic for log timestamps.
COLDARR_CONFIG
Path to the config file. Default /config/coldarr.yaml (via the image's /config working directory).

Web server

COLDARR_LISTEN_ADDR
Address serve listens on, in Go's host:port form - a bare port number is normalized to :port (all interfaces), but anything else must include the colon yourself, e.g. 0.0.0.0:8555 or 127.0.0.1:8555. Default :8478.
COLDARR_TLS_CERT_FILE / COLDARR_TLS_KEY_FILE
Certificate and private-key paths. Set both to make coldarr serve listen with HTTPS directly.
COLDARR_TRUSTED_REVERSE_PROXIES_CIDR (TRUSTED_REVERSE_PROXIES_CIDR)
Comma-separated proxy CIDRs whose Forwarded / X-Forwarded-Proto / X-Forwarded-Host headers should be trusted. Headers from other remote IPs are ignored. Coldarr refuses form submissions from other sites, which over plain HTTP it checks by comparing the page's address with the Host header. A plain-HTTP proxy that rewrites Host therefore needs to be listed here and send X-Forwarded-Host, or saving settings fails with a 403.

Sign-in

COLDARR_PASSWORD
Password that gates the GUI whenever OIDC is disabled. If unset (and COLDARR_PASSWORD_FILE isn't either), a random 64-character password is generated on every start and printed to the container's console log, with a warning that it won't survive a restart - set this to keep it stable. Irrelevant once OIDC is enabled.
COLDARR_PASSWORD_FILE
Path to a file containing the password (its contents win over COLDARR_PASSWORD if both are set) - point this at a Docker secret or any file you bind-mount into the container.
COLDARR_OIDC_ENABLED / COLDARR_OIDC_ISSUER_URL / COLDARR_OIDC_CLIENT_ID / COLDARR_OIDC_CLIENT_SECRET
OIDC auth overrides. When any are set, env values win over GUI-saved values. Set COLDARR_OIDC_ENABLED=false to disable OIDC entirely for troubleshooting.
COLDARR_OIDC_REDIRECT_URL / COLDARR_OIDC_REQUIRED_GROUP / COLDARR_OIDC_GROUPS_CLAIM
Optional OIDC details. Required group defaults to coldarr; groups claim defaults to groups.
COLDARR_OIDC_AUTO_LOGIN
Set to true/false to skip straight to the IdP instead of showing a login button, overriding the GUI-saved Settings > Auth checkbox. The login page shown right after signing out stays manual, so logging out doesn't sign you straight back in.
COLDARR_OIDC_CLIENT_SECRET_POST
Set to true for providers whose client registration uses token_endpoint_auth_method: client_secret_post (common with Authelia). The GUI exposes this as a checkbox under Settings > Auth.

Connections

RADARR_URL / RADARR_API_KEY (SONARR_*, LIDARR_*, JELLYFIN_*, JELLYFIN_ENABLED)
Connection overrides - see Connections.
QBITTORRENT_URL / QBITTORRENT_API_KEY / QBITTORRENT_USERNAME / QBITTORRENT_PASSWORD
qBittorrent connection overrides. The URL alone is enough when qBittorrent's WebUI skips authentication for Coldarr's address; otherwise set an API key (qBittorrent 5.2+) or a username and password.

Move timing

COLDARR_SETTLE_CHECK_INTERVAL / COLDARR_SETTLE_STABLE_CHECKS / COLDARR_SETTLE_MAX_WAIT
Tune how long apply waits for a move to actually land on disk before starting the next one queued for the same volume (Go duration strings, e.g. 5s/6h). Defaults suit typical local disks; raise MAX_WAIT for very large files on slow/network storage.
COLDARR_JELLYFIN_RESOLVE_TIMEOUT / COLDARR_JELLYFIN_RESOLVE_INTERVAL
How long apply waits, after the moves finish, for Jellyfin to index each item at its new path so its artwork can be refreshed (Go duration strings; defaults 15m / 10s, the interval backing off to a minute between polls). Raise the timeout if you see no Jellyfin item appeared at ... within in the logs - see Jellyfin.

Media paths

Tier paths must be bind-mounted at the same path Radarr/Sonarr use internally - Coldarr compares its own disk checks and the root folder paths the Arr APIs report as literal strings, so a mismatched mount looks like a misplaced or unavailable path.

Downloads tiers follow the same rule with qBittorrent: mount the download drive and seed storage at the same paths qBittorrent uses (if it saves to /downloads and seeds from /seed, so does Coldarr). Coldarr spots a download folder mounted from the wrong drive - qBittorrent lists finished downloads there that Coldarr can't see - and won't move anything to or from it. Read-only mounts are fine, unless you mark a downloads tier as not read-only to delete orphaned files in it: then that folder has to be mounted read-write. See Downloads and seeding.

Example compose file

Download the docker-compose.example.yml and edit the paths before starting Coldarr. The repository also includes an .env.example for optional environment overrides.

Complete compose example
docker-compose.example.yml
# Example Coldarr deployment. Copy to docker-compose.yml and adjust paths.
#
# Coldarr's default command is `serve` - a persistent web GUI on port 8478 for
# configuring connections/tiers, previewing plans, and applying them by
# hand. If you'd rather script it, override `command` to a one-shot CLI
# call instead (see the commented block below) and drive it from the
# host's own scheduler (cron/systemd timer):
#   0 4 * * * docker compose run --rm coldarr apply --yes
#
# The GUI can view connection status and trigger real moves, so it's
# never reachable unauthenticated: with no OIDC configured (the default),
# it falls back to a single shared password instead - see COLDARR_PASSWORD
# below. Enable OIDC in Settings -> Auth (or with COLDARR_OIDC_* env vars)
# for real identity-provider-backed login and group-based access instead.
#
# Connections (Radarr/Sonarr/Jellyfin URL + API key) can be configured
# either through the GUI (stored encrypted in ./config) or via env vars
# below - env vars always win if both are set. Secrets here come from a
# .env file next to this compose file - see .env.example.
services:
  coldarr:
    image: ghcr.io/voc0der/coldarr:latest
    container_name: coldarr
    ports:
      - "8478:8478"
    # Optional - start the container already as a specific uid:gid instead
    # of the default root-then-drop-to-PUID/PGID flow (useful for rootless
    # runtimes or policies that forbid containers starting as root, e.g. a
    # Kubernetes securityContext). When set, PUID/PGID below are ignored -
    # make sure this uid:gid already owns ./config.
    # user: "1000:1000"
    environment:
      PUID: "1000"
      PGID: "1000"
      TZ: ${TZ:-Etc/UTC}
      # Optional - serve HTTPS directly from Coldarr instead of plain HTTP.
      # COLDARR_TLS_CERT_FILE: /certs/fullchain.pem
      # COLDARR_TLS_KEY_FILE: /certs/privkey.pem
      # Optional - trust X-Forwarded-Proto/Host only from these proxy CIDRs.
      # COLDARR_TRUSTED_REVERSE_PROXIES_CIDR: 172.18.0.0/16,10.0.0.0/8
      # Optional - fixed password for when OIDC is disabled (below). Left
      # unset, one is generated and printed to `docker logs coldarr` on
      # every start, but won't survive a restart. COLDARR_PASSWORD_FILE
      # (path to a Docker secret or bind-mounted file, inside the
      # container) overrides this if both are set.
      # COLDARR_PASSWORD: ${COLDARR_PASSWORD}
      # COLDARR_PASSWORD_FILE: /run/secrets/coldarr_password
      # Optional - OIDC auth. These env vars override GUI-saved auth values.
      # COLDARR_OIDC_ENABLED: "true"
      # COLDARR_OIDC_ISSUER_URL: https://auth.example.com
      # COLDARR_OIDC_CLIENT_ID: coldarr
      # COLDARR_OIDC_CLIENT_SECRET: ${COLDARR_OIDC_CLIENT_SECRET}
      # COLDARR_OIDC_REDIRECT_URL: https://coldarr.example.com/auth/callback
      # COLDARR_OIDC_REQUIRED_GROUP: coldarr
      # COLDARR_OIDC_GROUPS_CLAIM: groups
      # COLDARR_OIDC_CLIENT_SECRET_POST: "true" # Authelia often wants this.
      # COLDARR_OIDC_AUTO_LOGIN: "false"
      # Optional - omit these and use the GUI's Connections page instead.
      # RADARR_URL: http://radarr:7878
      # RADARR_API_KEY: ${RADARR_API_KEY}
      # SONARR_URL: http://sonarr:8989
      # SONARR_API_KEY: ${SONARR_API_KEY}
      # JELLYFIN_URL: http://jellyfin:8096
      # JELLYFIN_API_KEY: ${JELLYFIN_API_KEY}
      # Optional - download tiering (see the docs' "Downloads and seeding").
      # The URL alone works if qBittorrent's WebUI skips authentication for
      # Coldarr's address; otherwise an API key (qBittorrent 5.2+) or a login.
      # QBITTORRENT_URL: http://qbittorrent:8080
      # QBITTORRENT_API_KEY: ${QBITTORRENT_API_KEY}
      # QBITTORRENT_USERNAME: admin
      # QBITTORRENT_PASSWORD: ${QBITTORRENT_PASSWORD}
      # LIDARR_URL: http://lidarr:8686
      # LIDARR_API_KEY: ${LIDARR_API_KEY}
    volumes:
      # Everything Coldarr persists lives here: coldarr.yaml, the
      # encrypted connection store + its key, and the move-history log.
      - ./config:/config
      # Optional - if COLDARR_TLS_CERT_FILE / COLDARR_TLS_KEY_FILE point here.
      # - ./certs:/certs:ro
      # Tier paths: mount at the SAME path Radarr/Sonarr use internally,
      # not just the same host path - Coldarr matches Arr API root folder
      # paths against these mounts literally. Coldarr only ever reads them
      # (disk usage checks, sizing candidates for a plan) - the actual
      # move is a moveFiles: true call to Radarr/Sonarr's own API, executed
      # by their containers against their own (separate, read-write) mount
      # of the same paths. :ro here costs Coldarr nothing and guarantees
      # it can't touch your media even if it tried.
      - /data/media:/data/media:ro
      - /mnt/sat1:/mnt/sat1:ro
      - /mnt/sat2:/mnt/sat2:ro
      # Optional - download tiering: the download drive and seed storage, at
      # the SAME paths qBittorrent uses. qBittorrent does the moving, so :ro
      # is fine - unless you mark a downloads tier as not read-only to delete
      # orphaned files from the Orphaned Storage page.
      # - /mnt/ssd/downloads:/downloads:ro
      # - /mnt/seed:/seed:ro
    restart: unless-stopped
    # If radarr/sonarr/jellyfin are containers in this same compose
    # project, put coldarr on their network instead of using service
    # names that only resolve there:
    # networks:
    #   - arr-stack

    # To run Coldarr as a one-shot CLI instead of the GUI, override the
    # command (and drop `ports`/`restart` above, or leave them - they're
    # harmless on a short-lived `run`):
    # command: ["apply", "--yes"]