--- title: Prowlarr FlareSolverr — Cloudflare-обход и починка индексеров created: '2026-09-20' updated: '2026-09-24' type: tech namespace: personal tags: [prowlarr, flaresolverr, cloudflare, rutracker, kinozal, torrent, arr, kraken, infra] related: - "[[family/how-to/arr-stack-kraken]]" - "[[personal/projects/media-pipeline-pitfalls]]" --- # Prowlarr + FlareSolverr — обход Cloudflare Зеркало ключевых питфоллов скилла `torrent-agent-pipeline` (§ Питфоллы). Контекст: 2026-09-20, Kraken. RuTracker / Kinozal / Kinozal (M) были «красными» в Prowlarr. ## Схема ``` Prowlarr (Kraken:9696) → FlareSolverr (Kraken:8191) → Cloudflare-защищённый трекер ⚠️ прокси применяется ТОЛЬКО при одинаковом tag на прокси И на индексере ``` Все контейнеры — в общей внешней сети `media_net`: radarr, sonarr, prowlarr, transmission, flaresolverr. Проверять фактическую сеть, а не compose-файл: ```bash ssh kraken 'sudo docker inspect -f "{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}" radarr sonarr prowlarr transmission flaresolverr' ``` ## 🔴 Главная засада: прокси без tag молча не применяется Созданный прокси (`POST /api/v1/indexerproxy`) с `tags: []` **не используется вообще** — в UI и логе Prowlarr об этом ни слова, запросы продолжают идти напрямую в Cloudflare. Прокси применяется к индексерам только когда у прокси И у каждого индексера стоит **один и тот же tag id**. ```bash KEY=$(grep -oP "(?<=)[^<]+" /srv/.../docker/prowlarr/config/config.xml) # 1. создать тег → получить id (обычно 1) curl -X POST -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \ -d '{"label":"flaresolverr"}' http://localhost:9696/prowlarr/api/v1/tag # 2. повесить tag на ПРОКСИ: GET целиком → jq .tags=[1] → PUT curl -s -H "X-Api-Key: $KEY" http://localhost:9696/prowlarr/api/v1/indexerproxy/1 | jq ".tags=[1]" > /tmp/p.json curl -X PUT -H "X-Api-Key: $KEY" -H "Content-Type: application/json" -d @/tmp/p.json \ http://localhost:9696/prowlarr/api/v1/indexerproxy/1 # 3. повесить ТОТ ЖЕ tag на индексера (RuTracker=1, Kinozal=5, Kinozal M=6) curl -s -H "X-Api-Key: $KEY" http://localhost:9696/prowlarr/api/v1/indexer/1 | jq ".tags=[1]" > /tmp/i.json curl -X PUT -H "X-Api-Key: $KEY" -H "Content-Type: application/json" -d @/tmp/i.json \ http://localhost:9696/prowlarr/api/v1/indexer/1 ``` **Пруф работы — только лог FlareSolverr, не лог и не UI Prowlarr:** ```bash ssh kraken 'sudo docker logs --tail 30 flaresolverr' # → Challenge detected. Title found: Just a moment... # → Challenge solved! ``` Непустой `GET /api/v1/indexerproxy` **не означает**, что прокси работает. Требуется Prowlarr ≥ 2.6 (в 2.5.2 прокси-модель только вводилась). ## 🔴 Блокировка индексера в SQLite — рестарт НЕ снимает Prowlarr сам выключает упавший индексер на 24ч (`Temporarily ignoring indexer X till +24h`), состояние в таблице `IndexerStatus` в `prowlarr.db`. `docker restart prowlarr` его **не сбрасывает** — запрос продолжает отдавать `429 Indexer is disabled till ...`. ```bash # бэкап перед правкой БД cp /srv/.../docker/prowlarr/config/prowlarr.db / ssh kraken 'sudo docker stop prowlarr' sqlite3 /srv/.../docker/prowlarr/config/prowlarr.db "DELETE FROM IndexerStatus WHERE ProviderId IN (1,5,6);" ssh kraken 'sudo docker start prowlarr' ``` `ProviderId` = id индексера. **В контейнерах `sqlite3` НЕТ** — на хосте Kraken `/usr/bin/sqlite3` ЕСТЬ. ## ⚠️ Безопасность: FlareSolverr логирует пароли в plaintext На `LOG_LEVEL=info` его лог печатает `postData` запроса, т.е. `login_username=...&login_password=...` **открытым текстом**. Ставить `LOG_LEVEL=warning` в compose. Проверка после понижения: `docker logs --since 5m flaresolverr | grep -c login_password` → `0`. ```yaml flaresolverr: image: ghcr.io/flaresolverr/flaresolverr:latest environment: - LOG_LEVEL=warning # ← не info: info пишет пароли трекеров - BROWSER_TIMEOUT=60000 ports: - 8191:8191 networks: - media_net ``` ## RuTor не фильтрует по категориям В caps заявлены `2000(Movies)/5000(TV)/8000(Other)`, но с `cat=5000` отдаёт **0** результатов, без `cat` — 100. Prowlarr сам пишет в полях индексера: *«RuTor does not return categories in its search results. To sync to your apps, include 8000(Other)»*. В Sonarr/Radarr ставить категорию RuTor = `8000`, не `5000`. ## 429 в Sonarr/Radarr от упавшего индексера `HTTP request failed: [429:TooManyRequests] [GET] at [http://prowlarr:9696/prowlarr/N/api?t=caps...]` — это **не** rate-limit трекера, а самоисключение Prowlarr. Не «ждать и повторять» — снимать `IndexerStatus`. ## Диагностические грабли - **`POST /api/v3/indexer/test` с `{"indexerId": N}` не работает** (Radarr v6.1.1, Sonarr, Prowlarr v2.5.2) → HTTP 400 с `validationFailures` (`'Name' must not be empty`, … `'Priority' must be between 1 and 50`). Эндпоинт валидирует тело как **новый** индексер. Ответ — массив, поэтому `jq '.validationFailures'` падает с `Cannot index array with string`. Рабочая альтернатива — `testall`: ```bash curl -s -X POST -H "X-Api-Key: $KEY" "http://localhost:7878/radarr/api/v3/indexer/testall" \ | jq -r '.[] | "\(.id)\t\(.isValid)\t\(.validationFailures[0].errorMessage // "")"' # Sonarr: порт 8989, UrlBase /sonarr ``` - **Prowlarr маскирует упавшие индексеры** — `GET /api/v1/health` и `/api/v1/indexerstatus` могут быть **пустыми**, пока индексер красный. Диагноз снимать из лога `/logs/prowlarr.debug.txt` (НЕ `logs/prowlarr.txt`). Искать: `grep -iE "rutracker|kinozal|Cloudflare|429"`. - **«Кто живой» быстрее всего — поиском**, не caps-тестом: ```bash curl -s -H "X-Api-Key: $KEY" "http://localhost:9696/prowlarr/api/v1/search?query=matrix&type=search" \ | jq -r 'group_by(.indexer)[] | "\(.[0].indexer): \(length)"' ``` По умолчанию до 100 результатов на индексер — «156 всего» ≠ «по 26 на каждый». ## 🔴 2026-09-24: FlareSolverr в crash-loop — Chrome не поднимается под chromedriver **Повод:** Alex — «Мастерская сатаны: в торрентах ничего не нашлось, по каким трекерам ищет и можно ли влиять через Prowlarr?» Симптом оказался двухслойным: сверху пустая выдача PidTor, под ней — мёртвый FlareSolverr. ### Слой 1 (верхний): PidTor отдаёт 503 из кэша, jac.red — 0 результатов ``` GET /lite/pidtor?title=Мастерская сатаны&year=2025 → HTTP 503, Content-Length: 0, X-Invoke-Cache: HIT, t=0.007s ← кэш GET http://jac.red/api/v2.0/indexers/all/results?title=Мастерская сатаны&year=2025 → HTTP 200, {"Results":[],"jacred":true} ← ЧЕСТНЫЙ ноль GET …/results?title=Матрица&year=1999 → HTTP 200, 132 раздачи ← контроль: живой ``` **jac.red знает 25 индексеров:** `anibelka anidub anifilm aniliberty animelayer anistar baibako bitru kinozal korsars leproduction lostfilm mazepa megapeer nnmclub rudub rutor rutracker selezen subsplease toloka torrentby ultradox viruseproject`. 🔴 **Список индексеров jac.red:** `GET http://jac.red/api/v2.0/indexers?configured=true` → 200, ~4 КБ, массив с `name`. Рабочий способ посмотреть, по каким трекерам реально идёт поиск — не догадываться. 🔴 **jac.red отдаёт 429 при частых запросах** (nginx): пауза `sleep 2-3` между вызовами обязательна. Формулировка ответа: nginx-страница `429 Too Many Requests`, НЕ JSON. 🔴 **`title=` должен быть URL-кодирован целиком.** `%20` в значении даёт **HTTP 400** — использовать `curl -G --data-urlencode "title=Мастерская сатаны"`. Ручная сборка query-строки с `%20` в середине ломается. **Вывод по «Мастерская сатаны»:** у фильма реально нет раздач на 25 трекерах jac.red. Механизм PidTor исправен (контроль на «Матрице» — 132 раздачи). Диагноз «сломан поиск» был бы неверным. ⚠️ Одноимённый фильм 2022 года лежит там как **«Мастерская дьявола / Devil's Workshop»** (`Devils Workshop` → 2 раздачи, bitru + kinozal, sid 0-1). Латиница в выдаче — не тот фильм, следить за годом. ### Слой 2 (первопричина): FlareSolverr crash-loop ⇒ 3 индексера Prowlarr мертвы ``` RuTracker (id=1) disabledTill = 2026-09-25T10:17:51Z Kinozal (id=5) disabledTill = 2026-09-25T10:17:50Z Kinozal (M) (id=6) disabledTill = 2026-09-25T10:17:48Z EscalationLevel = 9 (максимум — блокировка на 24 ч) ``` Проверка: `GET /prowlarr/1/api?t=search&q=…` → `429 Indexer is disabled till …`. Прямой результат несмотря на то, что таблица `IndexerStatus` начиналась 20-го (начальные падения) и докатилась до `EscalationLevel=9` именно **24-го в 10:17**. **В логе FlareSolverr:** ``` Error starting Chrome: Message: session not created: cannot connect to chrome at 127.0.0.1:48249 from chrome not reachable Exception: Error getting browser User-Agent. … File "/app/flaresolverr.py", line 136, in flaresolverr_service.test_browser_installation() ``` Контейнер `flaresolverr` уходит в **crash-loop** (`Restarting (1)`), HTTP `:8191` не отвечает (`Connection reset by peer`). Успешных решений челленджа в логе (`Challenge solved`) нет — считать их число: `sudo docker logs flaresolverr 2>&1 | grep -c "Challenge solved"`. ### Что проверено и исключено (не тратить время повторно) | Гипотеза | Проверка | Итог | |---|---|---| | Образ обновлён watchtower'ом и сломан | `image inspect` → v3.5.2, `Created=2026-09-12`; в логах watchtower нет строк про flaresolverr | ❌ не при чём | | Нехватка RAM | `free`, суммарный RSS всех процессов = 1.9 GB при MemAvailable ~1.8 GB | ⚠️ фон, не корень | | **swap мёртв** | `/proc/swaps` **пуст** при живом `/var/swap` (512 MB); `SwapFree` падал до 240 kB | ✅ **реальный баг хоста**, починен | | IPv6: `/etc/hosts` отдаёт `::1 localhost` первым, Chrome на IPv6-петле | в контейнере `/etc/hosts` → `::1 localhost` первый; **добавлен `net.ipv6.conf.all.disable_ipv6=1`** (как у prowlarr) → `disable_ipv6=1`, hosts стал `127.0.0.1 localhost` | ❌ **НЕ помогло**, ошибка дословно та же | | `/dev/shm` мал (64 MB) | `docker inspect` → `ShmSize=67108864`; в контейнере `df -h /dev/shm` → 64M | ⚠️ **подозреваемый №1 из оставшихся** | 🔴 **Chrome запускается вручную, но не под chromedriver.** Проверено: `docker exec flaresolverr sh -c '/usr/lib/chromium/chromium --headless=new --no-sandbox --disable-dev-shm-usage --disable-gpu --dump-dom about:blank'` → работает, отдаёт verbose-лог компонентов; под управлением selenium — `cannot connect to chrome at 127.0.0.1:`. В образе **`chromedriver` не в PATH** (`which chromedriver` → not found) — selenium ≥ 4 использует собственный Selenium Manager. 🔴 **Вывод не делать по одной гипотезе.** IPv6 выглядел убедительно (симптом «не видит chrome на 127.0.0.1» идеально ложится), но после применения ошибка воспроизвелась дословно — то есть причина не в резолве адреса. ### ✅ Починено в этой сессии **1. swap был мёртв — восстановлен.** `/proc/swaps` пуст, а `/var/swap` (512 MB) на диске есть, ядро держало `SwapFree=256 kB` при нулевом активном swap. Без swap Chrome не получает память при пике старта. ```bash sudo swapon /var/swap # → /proc/swaps: /var/swap file 524272 -2 cat /proc/swaps # проверка grep -E "^(SwapTotal|SwapFree)" /proc/meminfo ``` ⚠️ **`swapoff -a && swapon -a` НЕ восстанавливает** — даёт `SwapFree: 0 kB`. Помогает только явный `sudo swapon /var/swap`. В `/etc/fstab` строки для swap НЕТ (комментарий `dphys-swapfile`), systemd swap-unit не поднят (`systemctl list-units --type=swap --all` → 0 units). Автоподъём swap на этом хосте **не настроен** — после ребута swap снова может пропасть. **2. `disable_ipv6` добавлен сервису `flaresolverr`** (в `docker/prowlarr/docker-compose.yml`, бэкап `docker-compose.yml.bak-20260924-193632`). **Эффекта не дало** — оставлено как проверенная-неудачная попытка; при откате вернуть файл из бэкапа. ```yaml flaresolverr: image: ghcr.io/flaresolverr/flaresolverr:latest environment: - LOG_LEVEL=warning # ← не info: info пишет пароли трекеров - BROWSER_TIMEOUT=60000 ports: - 8191:8191 networks: - media_net sysctls: - net.ipv6.conf.all.disable_ipv6=1 # 2026-09-24: не помогло, см. выше shm_size: 1gb # ⏳ НЕ ПРИМЕНЕНО — кандидат на фикс ``` ⏳ **Незавершённое:** добавить `shm_size: 1gb` и пересоздать (`docker compose up -d --force-recreate flaresolverr`), затем снять блокировки `IndexerStatus` для id 1/5/6 (см. секцию выше) и проверить `POST http://localhost:8191/v1 {"cmd":"request.get","url":"https://kinozal.me/browse.php?c=0"}` → ожидается `status: ok`. После этого — поиск «Мастерская сатаны» по всем 6 трекерам. ### Дополнительные питфоллы сессии 1. 🔴 **`escape` из контейнера в crash-loop:** `docker exec` отдаёт `OCI runtime exec failed: exec failed: unable to start container process` / `cannot exec in a stopped container`. Это признак рестарт-петли, а не «неверный exec» — сначала `docker ps -a --filter name= --format "{{.Status}} R={{.RestartCount}}"`. 2. 🔴 **`sudo docker stats` показывает `0B / 0B` у всех контейнеров.** Метрика врёт (недоступна на этом хосте) — считать память по контейнерам через `/sys/fs/cgroup/system.slice/docker-.scope/memory.current`, а по процессам — `ps -eo rss`. 3. 🔴 **`ps aux --sort=-rss | awk` с `%4`/`$6` даёт мусор** — формат `ps aux` на этом хосте отдаёт 11 полей, `%MEM` и RSS нужно читать через `ps -eo user,pid,rss,comm,args`. 4. 🔴 **Долгие ssh-команды в crash-loop контейнера вешают терминал на 400+ с** — резать диагностику на короткие скрипты и не ждать готовности контейнера дольше 240 с. 5. ⚠️ **`sudo` на Кра́кене требует TTY** — `ssh -t kraken '…'`. Без `-t`: `sudo: a password is required`. ## Источники - Скилл: `torrent-agent-pipeline` (§ Питфоллы), `lampac-kraken-deploy` - Живой разбор: [[family/how-to/arr-stack-kraken]] — секции про починку индексера 2026-09-20 - Слой PidTor/кэш: [[personal/tech/lampac-torrserver]] §«2026-09-24» - Каталог/конфиг lampac: [[personal/tech/lampac-deployment]]