---
tags: [linux, tooling]
verified: 2026-09-02
---

# Медиасервер: Transmission + Sonarr/Radarr + Plex

## Обзор

Домашний медиасервер — цепочка из четырёх компонентов: торрент-клиент (Transmission) скачивает файлы, менеджеры контента (Sonarr для сериалов, Radarr для фильмов) автоматизируют поиск, загрузку и постобработку, агрегатор индексаторов (Prowlarr) проксирует поисковые запросы к трекерам, а медиасервер (Plex) предоставляет интерфейс для просмотра с метаданными и транскодингом.

Ключевая идея архитектуры — разделение хранилища на две зоны: `torrents/` (рабочая директория клиента, где файлы раздаются) и `library/` (организованная библиотека для Plex с правильными именами). Связь между ними — hardlink: файл физически один на диске, но доступен по двум путям. Это работает только в пределах одной файловой системы (и одного субтома в случае Btrfs).

## Архитектура

### Поток данных

```text
Prowlarr (индексаторы: rutracker, ...)
    ↓ проксирует поисковые запросы
Sonarr / Radarr (менеджеры контента)
    ↓ отправляет торрент
Transmission (загрузка)
    ↓ скачивает в /mnt/media/torrents/{series,films}/
Sonarr / Radarr (постобработка)
    ↓ создаёт hardlink с правильным именем
/mnt/media/library/{series,films}/
    ↓ сканирует
Plex (отдаёт клиентам)
```

### Структура каталогов

```text
/mnt/media/                        # точка монтирования (Btrfs)
├── torrents/                      # Transmission: рабочая директория
│   ├── films/                     # категория Radarr
│   ├── series/                    # категория Sonarr
│   ├── books/                     # ручные загрузки
│   ├── music/                     # ручные загрузки
│   └── .incomplete/               # незавершённые загрузки
├── library/                       # Plex: библиотека (hardlink)
│   ├── films/                     # Plex тип "Фильмы"
│   └── series/                    # Plex тип "Телепередачи"
├── books/                         # хранилище книг (вне Plex)
└── .torrent-files/                # бэкап .torrent файлов
```

### Hardlink — центральный механизм

Hardlink — второе имя для тех же данных на диске (тот же inode). В отличие от symlink, hardlink неотличим от «оригинала» — оба пути равноправны. Удаление одного не затрагивает другой; данные исчезают только когда удалены все ссылки на inode.

Ограничения: hardlink работает только внутри одной файловой системы и одного субтома Btrfs. Поэтому `torrents/` и `library/` должны находиться на одном субтоме. Книги и `.torrent-files/` можно вынести в отдельные субтома (они не нуждаются в hardlink) и снапшотить независимо.

Проверить, что файл — hardlink, а не копия:

```bash
stat file.mkv | grep Links   # Links: 2 означает hardlink
ls -li file1 file2           # одинаковый inode = один файл
```

#### Права на чтение мало — нужна запись

Ядро по умолчанию работает с `fs.protected_hardlinks=1`. При такой настройке жёсткую ссылку на файл разрешено создавать только если вызывающий им **владеет** либо имеет права на **чтение и запись**. Одного чтения не хватает.

Отсюда типичный сценарий: Transmission кладёт файл как `wes:wes 664`, Radarr работает в группе `media`, к чужому файлу у него остаются права `r--`, и `link()` возвращает `EPERM`. В логе это выглядит так:

```text
Debug|DiskProvider|Hardlink '/mnt/media/torrents/films/X.mkv' to
                   '/mnt/media/library/films/X (2024)/X (2024).mkv' failed.
   at NzbDrone.Mono.Disk.DiskProvider.TryCreateHardLink(...)
```

Соответственно, лечится не правами на каталог, а группой и битом setgid (см. раздел о правах доступа).

#### На Btrfs поломка не видна по свободному месту

Когда жёсткая ссылка не удалась, *Arr молча копирует файл. На Btrfs `File.Copy` в .NET идёт через `copy_file_range()`, а ядро превращает это в **reflink** — CoW-клон, у которого свой inode, но общие экстенты с оригиналом. Место при этом не тратится, и обычная проверка «сколько занято» ничего не покажет.

Ловушка в том, что `du`, вызванный по каждому каталогу отдельно, посчитает такие данные дважды:

```text
$ du -sh /mnt/media/library /mnt/media/torrents
758G  /mnt/media/library
730G  /mnt/media/torrents      # сумма 1.5 ТБ...

$ df -h /mnt/media
/dev/nvme0n1p1  932G  780G  152G  84%  /mnt/media   # ...а занято 778 ГБ
```

Расхождение почти ровно вдвое — признак, что данные разделены, но неизвестно чем: жёсткими ссылками или reflink. Различать их надо по числу ссылок и по экстентам:

```bash
# сколько файлов реально связаны жёстко (Links > 1)
find /mnt/media/library -type f -size +100M -printf '%n\n' | sort -n | uniq -c

# делят ли два конкретных файла одни экстенты
filefrag -v "library/films/X (2024)/X (2024).mkv" "torrents/films/X.mkv"
# одинаковый physical_offset и флаг shared = reflink
```

Практическая разница: reflink расходится при первой же перезаписи любой из копий, а на файловой системе без CoW (ext4, XFS) того же места просто не хватило бы. То есть на Btrfs сломанные hardlink маскируются, но чинить их всё равно надо.

### Права доступа

Все сервисы должны работать с общей группой `media`, чтобы файлы, созданные одним сервисом, были доступны другим:

```bash
sudo groupadd -f media
sudo usermod -aG media wes
sudo usermod -aG media sonarr
sudo usermod -aG media radarr
sudo usermod -aG media plex
```

Systemd-юниты Sonarr и Radarr должны содержать `Group=media`. Transmission (user-level unit) наследует группы пользователя `wes`. Umask `002` для всех сервисов обеспечивает права 775/664 — группа `media` может читать и писать.

```bash
sudo chgrp -R media /mnt/media
sudo chmod -R 775 /mnt/media
```

После изменения групп обязательно перезапустить все сервисы, иначе новая группа не подхватится.

#### Бит setgid обязателен, иначе всё разъедется обратно

Одного `chgrp -R` недостаточно: он чинит только те файлы, что есть сейчас. Новые файлы получают **основную группу процесса-создателя**, а не группу каталога. Transmission работает под `wes`, чья основная группа — `wes`, поэтому каждая свежая загрузка ложится как `wes:wes`, и группа `media` доступа к ней не имеет.

Лечится битом setgid на каталогах — с ним новые файлы наследуют группу каталога:

```bash
sudo find /mnt/media -type d -exec chmod g+s {} +
```

Проверка — в выводе `ls -ld` должна стоять `s` вместо `x` в правах группы:

```text
$ ls -ld /mnt/media/torrents
drwxrwsr-x 1 wes media 170 /mnt/media/torrents
       ↑ setgid на месте
```

Быстрый тест наследования:

```bash
touch /mnt/media/torrents/films/.test && \
  stat -c '%U:%G' /mnt/media/torrents/films/.test && \
  rm /mnt/media/torrents/films/.test
# должно напечатать wes:media, а не wes:wes
```

Альтернатива — прописать `media` основной группой Transmission (`Group=media` в юните или `usermod -g`), но setgid надёжнее: он работает и для файлов, положенных руками.

## Установка (CachyOS / Arch)

### Компоненты

```bash
# Transmission daemon (если не установлен)
sudo pacman -S transmission-cli

# Sonarr, Radarr, Prowlarr — бинарные пакеты из AUR
paru -S sonarr-bin radarr-bin prowlarr-bin

# Plex
paru -S plex-media-server
```

### Запуск сервисов

Transmission на CachyOS может работать как user-level systemd unit:

```bash
# проверить
systemctl --user status transmission
systemctl --user enable --now transmission.service

# НЕ использовать одновременно системный юнит — будут конфликты:
sudo systemctl disable --now transmission-daemon
```

Остальные сервисы — системные:

```bash
sudo systemctl enable --now sonarr radarr prowlarr plexmediaserver
```

Веб-интерфейсы:

| Сервис      | URL                        |
|-------------|----------------------------|
| Transmission| http://localhost:9091       |
| Sonarr      | http://localhost:8989       |
| Radarr      | http://localhost:7878       |
| Prowlarr    | http://localhost:9696       |
| Plex        | http://localhost:32400/web  |

## Настройка Transmission

Конфиг: `~/.config/transmission-daemon/settings.json`

Критически важно: daemon перезаписывает `settings.json` при остановке. Редактировать только при остановленном сервисе:

```bash
systemctl --user stop transmission.service
# ... редактирование ...
systemctl --user start transmission.service
```

Ключевые параметры:

```json
{
    "download-dir": "/mnt/media/torrents",
    "incomplete-dir": "/mnt/media/torrents/.incomplete",
    "incomplete-dir-enabled": true,
    "watch-dir": "/mnt/media/.torrent-files",
    "watch-dir-enabled": true,
    "umask": "002",
    "cache-size-mb": 16,
    "encryption": 2,
    "rpc-bind-address": "127.0.0.1",
    "rpc-authentication-required": false
}
```

`watch-dir` — бросаешь .torrent файл в эту папку, Transmission подхватывает автоматически. Файл не удаляется (`trash-original-torrent-files: false`), остаётся как бэкап.

`umask: "002"` — файлы создаются с правами 664/775, доступны группе `media`.

`encryption: 2` — требовать шифрование (отсекает часть пиров, но снижает вероятность инспекции трафика).

### Управление через CLI

```bash
# список торрентов
transmission-remote -l

# добавить торрент в конкретную категорию
transmission-remote --add file.torrent --download-dir /mnt/media/torrents/films
transmission-remote --add "magnet:?xt=..." --download-dir /mnt/media/torrents/series

# инфо по торренту
transmission-remote -t 1 -i

# переместить файлы торрента (раздача продолжится из нового места)
transmission-remote -t 1 --move /mnt/media/torrents/books

# удалить торрент (файлы остаются)
transmission-remote -t 1 -r

# удалить торрент и файлы
transmission-remote -t 1 -rad
```

## Настройка Prowlarr

Prowlarr — единый агрегатор индексаторов. Настраивается первым, потому что Sonarr и Radarr к нему подключаются.

1. `http://localhost:9696` → настроить аутентификацию (Forms, Disabled for Local Addresses)
2. Indexers → Add Indexer → найти rutracker.org → указать логин/пароль
3. Settings → Apps → добавить Sonarr (URL: `http://localhost:8989`, API Key из Sonarr → Settings → General)
4. Settings → Apps → добавить Radarr (URL: `http://localhost:7878`, API Key из Radarr → Settings → General)
5. На главной → Sync App Indexers — принудительный проброс индексаторов

После этого rutracker автоматически появится в Sonarr и Radarr как индексатор.

## Настройка Sonarr (сериалы)

### Download Client

Settings → Download Clients → «+» → Transmission:

- Host: localhost, Port: 9091
- Username/Password: пусто (если `rpc-authentication-required: false`)
- Category: `series` — Transmission создаст подкаталог `/mnt/media/torrents/series`
- Remove Completed: **выключить**, если хочешь продолжать раздавать

### Media Management

Settings → Media Management:

- **Rename Episodes**: включить — Sonarr будет переименовывать файлы при импорте
- Root Folders → Add: `/mnt/media/library/series`

### Профили качества

Settings → Profiles → выбрать/создать профиль (например HD-1080p). Здесь настраивается: какие форматы качества приемлемы, разрешён ли автоматический upgrade, и язык (в Sonarr v4 через Custom Formats).

## Настройка Radarr (фильмы)

Аналогично Sonarr:

- Download Client: Transmission, Category: `films`
- Media Management: Rename Movies — включить, Root Folder: `/mnt/media/library/films`

## Настройка Plex

`http://localhost:32400/web` → мастер настройки.

Библиотеки:

- Тип «Фильмы» → путь `/mnt/media/library/films`
- Тип «Телепередачи» (TV Shows) → путь `/mnt/media/library/series`

Plex работает от пользователя `plex`. Убедиться, что он в группе `media` и имеет доступ на чтение к `/mnt/media/library/`.

Одна оговорка про сам выбор Plex, появившаяся уже после того, как эта схема была собрана. С 30 апреля 2025 года воспроизведение личной медиатеки вне домашней сети перестало быть бесплатным: нужен либо Plex Pass у владельца сервера, либо Remote Watch Pass у того, кто смотрит. Ограничение включали не сразу — принудительное применение началось в конце ноября 2025 с Roku и в 2026 расползлось на Fire TV, Android TV, Apple TV и сторонние клиенты. Локального просмотра это не касается вовсе, так что описанная здесь схема работает как работала, но если сервер нужен и снаружи, выбор сужается до подписки или до Jellyfin, у которого удалённый доступ ничем не ограничен.

### Naming convention для Plex

Plex опирается на имена папок и файлов для скрапинга метаданных:

```text
Фильмы:   /library/films/Movie Name (Year)/Movie Name (Year).ext
Сериалы:  /library/series/Show Name/Season XX/Show Name - SxxExx - Episode Title.ext
```

Sonarr и Radarr создают hardlink с правильными именами автоматически при импорте — вручную переименовывать не нужно.

## Рабочие процессы

### Автоматический путь (Sonarr/Radarr находят раздачу)

1. Sonarr/Radarr → Add New → найти сериал/фильм
2. Указать Root Folder, профиль качества, что мониторить
3. Включить «Start search for missing» или запустить поиск вручную
4. Sonarr/Radarr найдёт раздачу через Prowlarr → отправит в Transmission
5. После скачивания — автоматический hardlink в `library/` с правильным именем
6. Plex подхватит при сканировании

Для ручного выбора конкретной раздачи (озвучка, качество): Interactive Search — иконка с человечком (не обычная лупа, которая хватает первую подходящую).

### Ручной путь (раздача не находится автоматически)

1. Добавить сериал/фильм в Sonarr/Radarr **без автопоиска**
2. Скачать .torrent с rutracker
3. Добавить в Transmission с указанием категории:

```bash
transmission-remote --add file.torrent --download-dir /mnt/media/torrents/series
# или /mnt/media/torrents/films для фильмов
```

4. Дождаться завершения загрузки
5. В Sonarr/Radarr: Wanted → Manual Import → указать путь к скачанным файлам → Interactive Import
6. Сопоставить файлы с эпизодами/фильмом (Series, Season, Episode)
7. Убедиться что внизу стоит **Hardlink/Copy Files** (не Move)
8. Import — hardlink создаётся в `library/`
9. Plex подхватит при сканировании

### Управление раздачами

После импорта торрент остаётся в Transmission и продолжает раздаваться из `torrents/`. В Sonarr Queue он может висеть как «не импортированный» — в этом случае: Queue → выбрать → Remove → **Ignore Downloads** (не Remove from Download Client, чтобы не удалить файлы и не прервать раздачу).

Когда раздача больше не нужна:

```bash
transmission-remote -t ID -r     # удалить торрент, файлы остаются
transmission-remote -t ID -rad   # удалить торрент и файлы из torrents/
```

При удалении файлов из `torrents/` — hardlink в `library/` продолжает жить (данные не удаляются, пока существует хотя бы одна ссылка на inode). И наоборот.

### Книги и прочий контент вне Plex

Книги не проходят через Sonarr/Radarr. Простой путь:

```bash
transmission-remote --add file.torrent --download-dir /mnt/media/torrents/books
```

Или переместить уже скачанный торрент:

```bash
transmission-remote -t ID --move /mnt/media/torrents/books
```

## Нюансы и подводные камни

### Cloudflare на трекере: FlareSolverr

RuTracker закрывает `/forum/login.php` управляемым челленджем Cloudflare. Prowlarr исполнять JavaScript не умеет и получает `403` с телом «Just a moment…», после чего уводит индексер в нарастающий бэкофф:

```text
Warn|IndexerHttpClient|HTTP Error - Res: HTTP/2.0 [POST]
     https://rutracker.org/forum/login.php: 403.Forbidden
Warn|RuTracker|RuTracker.org HTTP request failed: [403:Forbidden]
     at NzbDrone.Core.Indexers.Definitions.RuTracker.DoLogin()
```

Характерная деталь: `index.php` при этом отдаёт нормальные `200`. Челлендж висит именно на логине, поэтому «сайт открывается» — плохой тест. Проверять надо конкретный эндпоинт:

```bash
curl -s -o /dev/null -w '%{http_code}\n' https://rutracker.org/forum/index.php  # 200
curl -s -o /dev/null -w '%{http_code}\n' https://rutracker.org/forum/login.php  # 403
```

Ни User-Agent браузера, ни куки с главной страницы не помогают — челлендж настоящий, его надо исполнять. Решает [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr): он держит настоящий Chromium под виртуальным дисплеем, проходит проверку и отдаёт куку `cf_clearance`.

#### Headless не проходит, Xvfb проходит

Ключевой момент: Cloudflare детектит headless-режим. Проверить, решаем ли челлендж с конкретного IP, можно и без FlareSolverr — запустив обычный Chromium под Xvfb и посмотрев, появилась ли кука:

```bash
xvfb-run -a --server-args='-screen 0 1920x1080x24' \
    chromium --no-sandbox --user-data-dir=/tmp/chprof \
    https://rutracker.org/forum/login.php &
sleep 60; pkill -f 'user-data-dir=/tmp/chprof'

sqlite3 /tmp/chprof/Default/Cookies 'select host_key, name from cookies'
# наличие cf_clearance = челлендж решается, дело не в репутации IP
```

Если куки нет — проблема в самом адресе (репутация датацентрового IP), и FlareSolverr не спасёт.

#### Ставить сборку из исходников, не `-bin`

В AUR два пакета. `flaresolverr-bin` несёт внутри собственный Chrome, собранный под старую NSS; с актуальной системной `nss` проверка сертификатов уходит в бесконечную рекурсию и рендерер падает:

```text
ERROR  Error solving the challenge. Message: invalid session id:
       session deleted as the browser has closed the connection
```

В `coredumpctl` при этом видно `TrustStoreNSS::GetTrust` → `TrustStoreCollection::GetTrust` дважды подряд по одному адресу.

Пакет `flaresolverr` (из исходников) использует системный `chromium` и системный `chromedriver`. Версии в Arch выходят одним пакетом и всегда совпадают, NSS одна на всех — конфликт исчезает:

```bash
paru -Rns flaresolverr-bin
paru -S flaresolverr
sudo systemctl enable --now flaresolverr

journalctl -u flaresolverr | grep -E 'Chromium|Test'
#   Chrome / Chromium path: /usr/bin/chromium
#   Chrome / Chromium major version: 151
#   Test successful!
```

#### Подключение к Prowlarr

FlareSolverr добавляется как Indexer Proxy и действует **только на индексеры с тем же тегом**:

1. Settings → Indexers → Indexer Proxies → `+` → FlareSolverr, Host `http://localhost:8191/`, тег `flaresolverr`.
2. Тот же тег повесить на сам индексер (Indexers → RuTracker → Tags).

Без тега прокси не применится, и симптом не изменится. После настройки первый запрос занимает 20–30 секунд (решается челлендж), последующие — доли секунды, пока живёт сессия.

FlareSolverr по умолчанию слушает `0.0.0.0:8191`. Это прокси, скачивающий по запросу произвольный URL, поэтому наружу его лучше не выставлять:

```ini
# /etc/systemd/system/flaresolverr.service.d/bind-localhost.conf
[Service]
Environment="HOST=127.0.0.1"
```

### Теги Prowlarr: синхронизация в приложения

У поля Tags в Prowlarr два независимых смысла, и это легко перепутать:

| Где стоит тег | Что делает |
|---|---|
| Indexer Proxy + индексер | запросы к этому индексеру идут через прокси |
| Application (Sonarr/Radarr) + индексер | **только** помеченные индексеры уезжают в это приложение |

Второе — способ держать индексер в Prowlarr, но не отдавать его в *Arr. Полезно, когда определение трекера плохо раскладывает результаты по категориям: например, RuTor отдаёт всё как `8000/Other`, из-за чего Radarr либо отказывается сохранять индексер («Query successful, but no results in the configured categories»), либо принимает его и потом впустую опрашивает.

Важный побочный эффект: **как только у Application появился хоть один тег, непомеченные индексеры перестают синхронизироваться**. Новый индексер без тега молча не доедет до Sonarr и Radarr.

Ещё одна особенность: при `syncLevel = fullSync` Prowlarr перезаписывает настройки индексера в приложении. Руками поправленные категории живут до ближайшей синхронизации, потом откатываются.

### Сервисы должны переживать выход из сессии и ждать диск

Три ошибки уровня systemd, каждая проявляется не сразу.

**Transmission как user-сервис умирает вместе с сессией.** Sonarr и Radarr — системные и работают всегда, поэтому после разлогина они ломятся в мёртвый RPC:

```text
Warn|DownloadMonitoringService|Unable to retrieve queue and history items from Transmission
```

Лечится не переездом в системные юниты (это перенос данных и риск для раздач), а одной командой — она разрешает менеджеру `systemd --user` стартовать при загрузке и жить без сессии:

```bash
loginctl enable-linger $USER   # пароль не нужен, polkit разрешает для себя
```

**Никто не ждёт `/mnt/media`.** Если раздел не смонтировался, Plex пометит библиотеку недоступной, а *Arr увидят пустую корневую папку. Для системных юнитов:

```ini
# /etc/systemd/system/<сервис>.service.d/media-mount.conf
[Unit]
RequiresMountsFor=/mnt/media
```

Для пользовательских это не работает — менеджер `systemd --user` не видит системные mount-юниты. Там нужен цикл ожидания в `ExecStartPre`:

```ini
[Service]
ExecStartPre=/home/wes/bin/wait-mount.sh /mnt/media 60
```

**VPN стартует позже индексеров.** Если трекер доступен только через туннель, а `openvpn-client@…` поднимается вручную или просто медленнее, Prowlarr успевает сделать несколько неудачных запросов и уводит индексер в бэкофф. Порядок задаётся через `After=` плюс ожидание маршрута; скрипт ожидания должен возвращать `0` в любом случае, иначе сервис не стартует вовсе при выключенном VPN.

```ini
[Unit]
After=openvpn-client@deu.service

[Service]
ExecStartPre=/usr/local/bin/wait-vpn.sh 45
TimeoutStartSec=90
```

`TimeoutStartSec` здесь не украшение. Он отсчитывается на весь старт целиком, включая `ExecStartPre`, и если окажется меньше лимита ожидания — systemd убьёт скрипт раньше, чем тот успеет сдаться. Юнит навсегда залипает в `activating (start-pre)`, а `Restart=on-failure` превращает это в бесконечный цикл:

```text
prowlarr.service: start-pre operation timed out. Terminating.
prowlarr.service: Failed with result 'timeout'.
prowlarr.service: Scheduled restart job, restart counter is at 9.
```

Значение по умолчанию задаётся `DefaultTimeoutStartSec` и не универсально: в CachyOS это 15 секунд вместо привычных 90. Проверять надо у себя — `systemctl show -p DefaultTimeoutStartUSec`.

Вторая, более коварная половина проблемы: **`ExecStartPre` исполняется внутри песочницы юнита.** Пакеты *Arr приезжают с агрессивным хардненингом, и среди прочего там есть `SystemCallFilter=~@privileged` вместе с `SystemCallErrorNumber=EPERM`. iproute2 на старте вызывает `drop_cap()` — сбрасывает свои capabilities через `cap_set_proc()`, то есть через `capset()`, а этот вызов входит в `@privileged`. Фильтр возвращает `EPERM`, и `ip` уходит в `exit(EXIT_FAILURE)` прямо там: код 1, пустой вывод, ни строчки в stderr — при живом туннеле. Проверка вида `ip route show 0.0.0.0/1 | grep -q tun0` не срабатывает никогда, и скрипт честно ждёт весь лимит.

Отдельно стоит запомнить, почему это так тяжело поймать руками: `drop_cap()` выполняется только при `uid != 0 && euid != 0`. Под root та же команда в той же песочнице отработает нормально, и проверка «а если запустить вручную» ничего не покажет — воспроизводится только под учёткой сервиса.

Диагностируется воспроизведением песочницы в user-менеджере, по одной опции за раз:

```bash
systemd-run --user -q --pipe --wait \
  -p SystemCallFilter=@system-service -p SystemCallFilter=~@privileged \
  -p SystemCallErrorNumber=EPERM \
  ip route show 0.0.0.0/1
# rc=1, вывод пуст; без ~@privileged — rc=0 и маршрут на месте
```

Лечится отказом от внешних утилит в пользу `/proc/net/route`: чтение файла не требует ни одного привилегированного вызова и работает в любой песочнице. Адрес и маска записаны шестнадцатерично в порядке little-endian, поэтому `0.0.0.0/1` — это `Destination=00000000` и `Mask=00000080`:

```bash
#!/bin/sh
# Ждёт маршрут через tun0, максимум N секунд. Код возврата всегда 0.
limit=${1:-45}
n=0
while [ "$n" -lt "$limit" ]; do
    if awk '$1=="tun0" && $2=="00000000" && $8=="00000080" {found=1}
            END {exit !found}' /proc/net/route 2>/dev/null; then
        exit 0
    fi
    n=$((n + 1))
    sleep 1
done
exit 0
```

Обобщение шире одного скрипта: в `ExecStartPre` захардненного юнита нельзя рассчитывать на `ip`, `iptables` и прочие утилиты, работающие с capabilities. Либо читать `/proc` и `/sys` напрямую, либо выносить команду из песочницы префиксом `+` в `ExecStartPre=`.

### Тишина вместо уведомлений

По умолчанию в Sonarr, Radarr и Prowlarr не настроено ни одного Connection, а health-статус виден только в веб-морде. Отказ единственного индексера в такой конфигурации может тянуться неделями — формально всё «работает», просто ничего не находится.

Минимум, который стоит поднять сразу: любой Connection на события Health Issue и Health Restored, либо внешний опрос `/api/v3/health`:

```bash
curl -s -H "X-Api-Key: $KEY" http://localhost:8989/api/v3/health
```

Ответ `[]` — всё чисто. Иначе список объектов с полями `type` (`warning` / `error`) и `message`.

### Transmission daemon и конфиг

Transmission перезаписывает `settings.json` при остановке. Если редактировать при работающем daemon — изменения затрутся при рестарте. Всегда: stop → edit → start.

На CachyOS может быть два экземпляра: user-level (`systemctl --user`) и system-level (`sudo systemctl`). Они конфликтуют за RPC-порт. Использовать только один, второй отключить.

### Sonarr и нестандартные имена файлов

Sonarr сопоставляет файлы с эпизодами по имени. Если раздача названа нестандартно (номер без префикса `S01E`, русское название, нумерация через точку), автоматический импорт может не сработать — Sonarr интерпретирует `01.` как номер сезона, а не эпизода.

Решение: Manual Import → Interactive Import → вручную указать Series, Season, Episode для каждого файла.

### Выбор озвучки

Sonarr/Radarr выбирают раздачу по критериям качества и размера, не различая озвучки. Для контроля:

- **Interactive Search** (иконка с человечком) — показывает все найденные раздачи, по названию видна озвучка
- **Custom Formats** — создать формат с условием «Release Title contains: Кураж-Бамбей» (или другой перевод), назначить положительный score в профиле качества

Для русскоязычного контента с rutracker Manual/Interactive Search часто надёжнее полной автоматики.

### Аниме

При добавлении аниме-сериала в Sonarr: Series Type → **Anime** (вместо Standard). Это меняет логику нумерации на абсолютную и формат поисковых запросов. Дополнительно можно добавить аниме-индексаторы (Nyaa, AniDex) в Prowlarr.

### Btrfs: субтома и hardlink

Hardlink не работает между разными субтомами Btrfs. `torrents/` и `library/` должны быть в одном субтоме. `books/` и `.torrent-files/` можно вынести в отдельные субтома для независимого снапшотирования.

Для медиафайлов (уже сжатые кодеки H.264/H.265) — `compress=zstd` на уровне субтома бесполезен, только тратит CPU. Для книг и .torrent-файлов сжатие полезно.

## Источники

- [Servarr Wiki — Sonarr](https://wiki.servarr.com/sonarr)
- [Servarr Wiki — Radarr](https://wiki.servarr.com/radarr)
- [Servarr Wiki — Prowlarr](https://wiki.servarr.com/prowlarr)
- [Plex Naming Convention](https://support.plex.tv/articles/naming-and-organizing-your-tv-show-files/)
- [Arch Wiki — Plex](https://wiki.archlinux.org/title/Plex)
- [Arch Wiki — Transmission](https://wiki.archlinux.org/title/Transmission)
- [TRaSH Guides — Hardlinks](https://trash-guides.info/Hardlinks/Hardlinks-and-Instant-Moves/) — подробно о настройке hardlink в *Arr

---

## Связанные заметки

- [Linux — systemd: init, сервисы, таймеры](./linux-systemd.md) — запуск и изоляция сервисов медиасервера
- [Домашний десктоп на CachyOS](./setups/cachyos-desktop/hyprland-desktop.md) — окружение десктопа: панель и автозапуск, из которых удобно следить за статусом этих сервисов
- [Linux — файловые системы: ext4, XFS, Btrfs, LVM](./linux-filesystems.md) — hardlink, субтомы Btrfs и границы файловых систем
- [Linux — сеть: стек, конфигурация, firewall](./linux-networking.md) — проброс портов и ограничение доступа
- [yt-dlp: извлечение субтитров](./yt-dlp.md) — смежный сценарий работы с медиа
- [Обратный прокси и TLS: nginx vs Caddy](./reverse-proxy-tls.md) — как выставить эти сервисы наружу под именами и по HTTPS
- [Самохостинг за NAT: VPS как точка входа](./selfhosting-behind-nat.md) — как отдать эти сервисы наружу и почему медиа через VPS считать по трафику
- [Резервное копирование: стратегия и проверка восстановления](./backup-strategy.md) — что из этой установки бэкапить, а что перекачивается
