AI agent guide

A setup URL a user can give to an AI agent.

If a user sent you this page, read it first, inspect their existing Docker Compose stack, collect only the missing inputs, then install and validate putiorr for their Radarr, Sonarr, Lidarr, and Prowlarr setup.

Copyable agent brief

Give this compact brief to an assistant or sub-agent, then let it use the rest of this page for the full setup details.

Read https://ptheofan.github.io/putiorr/agent-setup.html first.
Goal: install putiorr as a Transmission-compatible put.io bridge.
Use Docker Compose with ghcr.io/ptheofan/putiorr:latest.
Mount persistent config at /data/putiorr-config.
Mount the host download target, for example /volume/downloads, as /downloads in putiorr and every *arr container.
Do not configure Remote Path Mapping when paths match.
Use PUTIORR_TARGET_DIR=/downloads and PUTIORR_LISTEN_PORT=9091.
Add PUTIORR_PROFILES_JSON to seed Radarr, Sonarr, and Lidarr profiles on first startup.
Keep putiorr on the same Docker network as Radarr, Sonarr, and Lidarr.
In each *arr app add a Transmission client:
  Host putiorr, Port 9091, SSL off, Directory /downloads.
  Category radarr/sonarr/lidarr.
  URL Base /radarr/transmission, /sonarr/transmission, or /lidarr/transmission.
Connect put.io through the putiorr UI using OAuth or Direct Token.
Validate with each *arr Test button and a small completed grab.

Agent rules

Public handoff URL

A user can give this URL to Codex or another assistant: https://ptheofan.github.io/putiorr/agent-setup.html. Treat this page as the authoritative setup brief for putiorr.

Operating rules

Do the setup with the user, not by guessing their storage or secrets. The stable defaults are one putiorr service, one persistent config mount, and one shared download target mounted at /downloads inside putiorr and every *arr app.

  • Never ask the user to paste a put.io token into chat unless they explicitly choose that path. Prefer browser OAuth or let the user paste a direct token into the putiorr UI.
  • Do not add custom Remote Path Mapping when the same host download folder can be mounted at the same container path.
  • Do not use local development port remaps such as 17010:9091 unless the user explicitly wants that host port.
  • Keep the putiorr service on the same Docker network as Radarr, Sonarr, and Lidarr so the *arr apps can use host putiorr and port 9091.
  • After editing Compose or app settings, run the validation checklist and report exactly what passed or what remains blocked.

Inputs to collect

Ask for these values or inspect them from the user's existing Compose files before editing anything.

Input Default or example Why it matters
Compose project path docker-compose.yml Where the putiorr service and shared network should be added.
*arr service names radarr, sonarr, lidarr Confirms which apps need profiles and download-client values.
putiorr config host path /volume1/docker/putiorr-config Stores SQLite state and UI-saved put.io credentials.
Download target host path /volume/downloads Must be mounted into putiorr and each *arr app at /downloads.
Media library host paths /volume/media/movies, /volume/media/series, /volume/media/music Final import locations for Radarr, Sonarr, and Lidarr.
put.io auth method Hosted OAuth relay or Direct Token Determines whether the user connects through the browser flow or pastes a token in the UI.
RPC auth Unset by default If set, the same username and password must be entered in each *arr download client.

Compose changes

Add putiorr beside the existing *arr services, or attach it to the same external Docker network. A published port is optional for browser access and is not required for container-to-container RPC.

services:
  putiorr:
    image: ghcr.io/ptheofan/putiorr:latest
    restart: unless-stopped
    environment:
      TZ: Europe/Athens
      PUTIORR_LISTEN_HOST: 0.0.0.0
      PUTIORR_LISTEN_PORT: 9091
      PUTIORR_STATE_PATH: /data/putiorr-config/putiorr.sqlite
      PUTIORR_TARGET_DIR: /downloads
      PUTIORR_PROFILES_JSON: >-
        [
          {
            "name": "Radarr",
            "type": "radarr",
            "slug": "radarr",
            "putio_folder_name": "putiorr",
            "downloadAt": "/downloads",
            "rpc_path": "/radarr/transmission/rpc",
            "enabled": true
          },
          {
            "name": "Sonarr",
            "type": "sonarr",
            "slug": "sonarr",
            "putio_folder_name": "putiorr",
            "downloadAt": "/downloads",
            "rpc_path": "/sonarr/transmission/rpc",
            "enabled": true
          },
          {
            "name": "Lidarr",
            "type": "lidarr",
            "slug": "lidarr",
            "putio_folder_name": "putiorr",
            "downloadAt": "/downloads",
            "rpc_path": "/lidarr/transmission/rpc",
            "enabled": true
          }
        ]
      PUTIORR_WORKERS: "4"
      PUTIORR_CLEANUP_REMOTE_FILES: "true"
    volumes:
      - /volume1/docker/putiorr-config:/data/putiorr-config
      - /volume/downloads:/downloads

Make sure each importer also has the same download target mount. The left side may change for the user's host, but the right side should stay /downloads everywhere.

services:
  radarr:
    volumes:
      - /volume/downloads:/downloads
      - /volume/media/movies:/movies

  sonarr:
    volumes:
      - /volume/downloads:/downloads
      - /volume/media/series:/series

  lidarr:
    volumes:
      - /volume/downloads:/downloads
      - /volume/media/music:/music

Initial JSON setup

Use PUTIORR_PROFILES_JSON to create the initial RR profiles automatically on first startup, so the user does not have to create Radarr, Sonarr, and Lidarr profiles in the UI.

Important behavior

  • The value must be a valid JSON array, usually embedded in Compose as a YAML folded scalar with >-.
  • putiorr reads it from the putiorr service environment when the container starts.
  • The seed runs only when there are no existing RR profiles in the SQLite state database.
  • A default download profile is created automatically, and seeded RR profiles attach to it unless download_profile_id is supplied.
  • Use absolute container paths such as /downloads. Do not seed host paths such as /volume/downloads into profile downloadAt.
PUTIORR_PROFILES_JSON: >-
  [
    {
      "name": "Radarr",
      "type": "radarr",
      "slug": "radarr",
      "putio_folder_name": "putiorr",
      "downloadAt": "/downloads",
      "rpc_path": "/radarr/transmission/rpc",
      "enabled": true
    },
    {
      "name": "Sonarr",
      "type": "sonarr",
      "slug": "sonarr",
      "putio_folder_name": "putiorr",
      "downloadAt": "/downloads",
      "rpc_path": "/sonarr/transmission/rpc",
      "enabled": true
    },
    {
      "name": "Lidarr",
      "type": "lidarr",
      "slug": "lidarr",
      "putio_folder_name": "putiorr",
      "downloadAt": "/downloads",
      "rpc_path": "/lidarr/transmission/rpc",
      "enabled": true
    }
  ]

The profile fields map directly to the startup seed: name is the UI display name, type and slug identify the app, putio_folder_name is the put.io folder, downloadAt is the container download target, and rpc_path is the endpoint used by the matching *arr app.

If the user already has a populated SQLite state database, do not assume changing PUTIORR_PROFILES_JSON will update existing profiles. Use the UI or the profile API instead.

putiorr profiles

After the container starts, open the putiorr UI and confirm the seeded RR profiles. If seed JSON was not used, create one profile per importer. The UI's field guide should be used to confirm paths before saving.

Profile field Radarr Sonarr Lidarr
App preset Radarr Sonarr Lidarr
Display name Radarr Sonarr Lidarr
Put.io destination folder putiorr putiorr putiorr
Shared download folder /downloads /downloads /downloads
RPC endpoint path /radarr/transmission/rpc /sonarr/transmission/rpc /lidarr/transmission/rpc
Host from *arr container putiorr putiorr putiorr
Port 9091 9091 9091

Link each RR profile to a download profile after creating custom download profiles. The default download profile is acceptable for a first setup.

*arr download-client values

Add a Transmission download client in Radarr, Sonarr, and Lidarr. The Name can be putiorr; the Host is the Docker service name putiorr when containers share a network.

Field Radarr Sonarr Lidarr
Name putiorr putiorr putiorr
Host putiorr putiorr putiorr
Port 9091 9091 9091
Use SSL Off Off Off
Category radarr sonarr lidarr
Directory /downloads /downloads /downloads
URL Base /radarr/transmission /sonarr/transmission /lidarr/transmission

Keep Completed Download Handling enabled. Prowlarr usually does not need its own putiorr profile because Radarr, Sonarr, and Lidarr send accepted grabs to putiorr.

Validation

  • Run docker compose ps and confirm putiorr and the *arr apps are healthy.
  • Open the putiorr UI and confirm put.io is connected.
  • Confirm the seeded profiles exist with GET /api/profiles or in the UI before configuring the *arr apps.
  • Use each *arr app's download-client Test button. A host lookup failure usually means the containers do not share a Docker network.
  • Confirm the *arr app sees Directory /downloads and Category radarr, sonarr, or lidarr.
  • Trigger a small grab and verify the putiorr dashboard shows remote transfer progress, then local download progress.
  • Confirm completed files appear under /downloads/<category> from inside the relevant *arr container.

Troubleshooting

*arr cannot connect

Check that putiorr and the *arr container share a Docker network. Use Host putiorr, Port 9091, and SSL off.

Import cannot find files

Check path identity first. The same host folder must be mounted as /downloads in putiorr and the importer.

OAuth is blocked

Use the Direct Token tab in the putiorr UI, or configure a self-hosted callback with PUTIORR_PUBLIC_URL.