SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-28

Настройка стека arr в одном Docker Compose файле

Разверните Prowlarr, Sonarr, Radarr и qBittorrent в одном Docker Compose файле. Настройте PUID, PGID и структуру томов для корректной работы жестких ссылок и прав доступа.

Что вы создаете

Стек Docker Compose для медиабиблиотеки состоит из четырех контейнеров: Prowlarr для управления индексаторами, Sonarr для сериалов, Radarr для фильмов и qBittorrent в качестве клиента для загрузки. Они взаимодействуют друг с другом внутри сети Compose по именам сервисов и используют общую структуру папок на хосте. Установка выполняется быстро. Основной фактор, определяющий, будет ли стек работать годами или создавать проблемы каждую неделю, — это схема размещения томов, поэтому большая часть руководства посвящена именно ей.

Стек не занимается автоматическим поиском контента. Prowlarr хранит только те индексаторы, которые вы добавили самостоятельно; выбор индексаторов и юридическая ответственность за их использование лежат на вас. Данное руководство описывает техническую реализацию: пользователей, пути, права доступа, сетевое взаимодействие контейнеров и проверки работоспособности.

Если вы ранее не работали с файлами Compose, сначала изучите основы Docker Compose для VPS. В этой статье предполагается, что команда docker compose version уже выводит данные на вашем сервере.

Почему жесткие ссылки перестают работать и почему это критически важно

Когда Sonarr завершает загрузку, он импортирует файл в вашу библиотеку. Если папка загрузок и папка библиотеки находятся в одной файловой системе, импорт выполняется через жесткую ссылку (hardlink): это второе имя, указывающее на те же данные на диске. Это не занимает дополнительного места и не требует времени. Торрент продолжает раздаваться под старым именем, пока ваш медиасервер считывает файл по новому пути.

Если эти две папки находятся в разных файловых системах, ядро не может создать такую ссылку. Sonarr переключается на копирование. Сезон объемом 40 GB теперь занимает 80 GB на диске и требует нескольких минут на операции ввода-вывода, а в логе импорта фиксируется, что жесткая ссылка не удалась и файл был скопирован. На VPS с ограниченным дисковым пространством именно так пользователи исчерпывают место за неделю.

В этом и заключается ловушка. Внутри контейнера bind mount является границей файловой системы. Если примонтировать /mnt/data/torrents как /downloads, а /mnt/data/media как /tv, то даже при условии, что оба пути находятся на одном физическом диске хоста, Sonarr увидит два отдельных раздела и откажется создавать ссылки между ними. Официальная документация к образу LinuxServer.io прямо указывает на это: использование раздельных путей /downloads и /tv лишает возможности использовать жесткие ссылки.

Решение заключается в использовании одного монтирования. Каждый контейнер, работающий с медиафайлами, должен получать один и тот же том /mnt/data:/data, а все используемые ими пути должны быть папками внутри него. Одна точка монтирования, одна файловая система — и жесткие ссылки работают корректно.

Создание пользователя, группы и каталогов

Контейнеры записывают файлы от имени числового идентификатора пользователя (UID), который задается параметрами PUID и PGID. Используйте свою учетную запись, чтобы иметь возможность читать и редактировать эти файлы по SSH без использования sudo.

id -u
id -g

Обе команды обычно выводят 1000 на свежей установке Ubuntu VPS. Теперь создайте дерево каталогов. Разместите его на том диске, где хранятся ваши медиафайлы, и держите всю структуру на одном этом диске.

sudo mkdir -p /mnt/data/torrents/movies /mnt/data/torrents/tv
sudo mkdir -p /mnt/data/media/Movies /mnt/data/media/Shows
sudo chown -R 1000:1000 /mnt/data
sudo chmod -R 775 /mnt/data

Перед продолжением убедитесь, что это действительно одна файловая система:

df --output=source,target /mnt/data/torrents /mnt/data/media

Обе строки должны указывать на одно и то же устройство-источник. Если устройства разные, жесткие ссылки (hardlinks) работать не будут, независимо от настроек в конфигурации контейнера.

Каталоги библиотеки намеренно названы Movies и Shows. Если вы уже используете Jellyfin в качестве медиасервера, смонтируйте /mnt/data/media в Jellyfin как /media, тогда его библиотеки окажутся в /media/Movies и /media/Shows — именно там, где их предполагает разместить данное руководство.

Файл окружения

Храните значения, которые различаются в зависимости от сервера, в .env рядом с файлом Compose.

mkdir -p ~/arr && cd ~/arr

Создайте ~/arr/.env:

PUID=1000
PGID=1000
TZ=Etc/UTC
DATA_ROOT=/mnt/data

Установите TZ в соответствии с вашей зоной, например Europe/Berlin. Приложения arr планируют задачи и помечают строки логов в этой зоне, поэтому неверное значение в дальнейшем затруднит анализ логов.

Файл Compose

Запишите ~/arr/docker-compose.yml:

services:
  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./config/prowlarr:/config
    ports:
      - 127.0.0.1:9696:9696
    restart: unless-stopped

  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./config/sonarr:/config
      - ${DATA_ROOT}:/data
    ports:
      - 127.0.0.1:8989:8989
    restart: unless-stopped

  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./config/radarr:/config
      - ${DATA_ROOT}:/data
    ports:
      - 127.0.0.1:7878:7878
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - WEBUI_PORT=8080
      - TORRENTING_PORT=6881
    volumes:
      - ./config/qbittorrent:/config
      - ${DATA_ROOT}:/data
    ports:
      - 127.0.0.1:8080:8080
      - 6881:6881
      - 6881:6881/udp
    stop_grace_period: "10s"
    restart: unless-stopped

Четыре элемента в этом файле выполняют основную работу.

${DATA_ROOT}:/data идентичен во всех трех контейнерах, которые работают с медиафайлами. Prowlarr его не использует, так как Prowlarr никогда не открывает медиафайлы.

Каждый веб-порт привязан к 127.0.0.1, поэтому Docker публикует его только на адресе loopback. Обычная запись 8989:8989 опубликовала бы его на всех интерфейсах, и собственные правила брандмауэра Docker пропустили бы этот трафик в обход правила ufw deny. Такое поведение постоянно становится неожиданностью для пользователей, оно подробно описано в почему Docker публикует порты в обход ufw.

Порт 6881 намеренно опубликован на всех интерфейсах. Это порт для прослушивания torrent-клиента, и он должен быть доступен для входящих соединений от пиров. Разрешите его с помощью sudo ufw allow 6881; ознакомьтесь с основами работы с брандмауэром ufw на VPS, если эта команда вам не знакома.

Каталоги конфигурации разделены для каждого приложения, совместно используется только том с медиафайлами. Создайте их перед первым запуском, чтобы владельцем был ваш пользователь, а не root:

mkdir -p ~/arr/config/prowlarr ~/arr/config/sonarr ~/arr/config/radarr ~/arr/config/qbittorrent
docker compose up -d
docker compose ps

Все четыре сервиса должны считывать running. По состоянию на июль 2026 года эти образы публикуются на lscr.io, а тег latest соответствует текущему стабильному релизу. Если вы хотите, чтобы обновления были осознанным решением, а не сюрпризом, зафиксируйте конкретный тег версии.

Безопасный доступ к веб-интерфейсам

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

ssh -L 9696:127.0.0.1:9696 -L 8989:127.0.0.1:8989 \
    -L 7878:127.0.0.1:7878 -L 8080:127.0.0.1:8080 you@your-server

Теперь по адресу http://127.0.0.1:8989 в браузере можно получить доступ к Sonarr на сервере. Для постоянного доступа разместите стек за Traefik с TLS-сертификатами для нескольких приложений или подключайтесь к серверу через собственный VPN-сервер WireGuard. Ни одно из этих приложений не должно быть доступно из публичного интернета, если перед ним стоит только собственная страница входа. Если вы выбрали вариант с reverse proxy и хотите использовать одну учетную запись для всех четырех интерфейсов вместо четырех отдельных логинов, Authentik предоставляет систему единого входа (SSO), которую Traefik может принудительно применять для каждого запроса через механизм forward auth.

qBittorrent при первом запуске генерирует случайный пароль администратора и выводит его в лог контейнера. Прочитайте его, а затем измените в веб-интерфейсе:

docker compose logs qbittorrent | grep -i password

Если вы не измените пароль, при каждом перезапуске будет генерироваться новый случайный пароль, и вам придется каждый раз обращаться к логам.

Настройка путей внутри каждого приложения

В qBittorrent откройте Options, затем Downloads и установите путь сохранения по умолчанию в /data/torrents. Папку для незавершенных загрузок разместите в том же дереве, например, в /data/torrents/incomplete. Загрузка, завершенная вне /data, не может быть добавлена в библиотеку с помощью жесткой ссылки (hardlink).

В Sonarr откройте Settings, затем Media Management и добавьте корневую папку /data/media/Shows. В Radarr корневая папка — /data/media/Movies. Это пути внутри контейнера. Хостовый путь /mnt/data/media/Shows будет отклонен, так как с точки зрения контейнера этого каталога не существует.

И в Sonarr, и в Radarr откройте Settings, затем Download Clients и добавьте qBittorrent. Хост — qbittorrent, порт — 8080. Имя сервиса работает как имя хоста, поскольку Compose помещает все четыре контейнера в одну сеть с внутренней службой DNS (domain name system). Не используйте здесь localhost: внутри контейнера Sonarr адрес localhost указывает на сам Sonarr.

Оставьте Remote Path Mappings пустым. Эта функция нужна для преобразования пути, о котором сообщает клиент загрузки, в путь, доступный приложению arr. При использовании одного общего монтирования /data оба контейнера уже «видят» пути одинаково — это вторая причина, по которой стоит придерживаться такой структуры.

Подключение Prowlarr к Sonarr и Radarr

Prowlarr передает настройки индексаторов в другие приложения, поэтому вам достаточно настроить индексатор один раз вместо двух. Для этого потребуется API (application programming interface) ключ от каждого приложения.

В Sonarr откройте Settings, затем General и скопируйте API key. В Prowlarr откройте Settings, затем Apps, добавьте приложение Sonarr и заполните три поля. Prowlarr Server — это http://prowlarr:9696. Sonarr Server — это http://sonarr:8989. API Key — это значение, которое вы скопировали. Нажмите Test. Зеленый индикатор означает, что Prowlarr успешно связался с Sonarr через сеть Compose. Повторите те же действия для Radarr по адресу http://radarr:7878.

Красный индикатор с сообщением о том, что в соединении отказано, почти всегда означает неверное имя сервиса или отсутствие префикса http://. Проверьте, разрешается ли имя внутри контейнера:

docker compose exec prowlarr curl -sS -o /dev/null -w '%{http_code}\n' http://sonarr:8989

HTTP-код ответа подтверждает, что сетевой путь корректен. Ошибка разрешения имени означает, что имя сервиса указано неверно.

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

Не доверяйте настройке, пока не проверите счетчик ссылок. После импорта одного элемента сравните загруженный файл с файлом в библиотеке:

stat -c '%i %h %n' /mnt/data/torrents/tv/*/*.mkv
stat -c '%i %h %n' /mnt/data/media/Shows/*/*/*.mkv

Первое число — это inode, второе — счетчик ссылок. Файл, для которого создана жесткая ссылка, имеет одинаковый inode в обоих местах и счетчик ссылок 2. Два разных inode, каждый со счетчиком 1, означают, что Sonarr скопировал файл, а в журнале импорта будет указано, что создание жесткой ссылки завершилось неудачей.

Также следите за диском. Показатель df -h /mnt/data должен почти не меняться при импорте, так как жесткая ссылка добавляет только имя, а не данные.

Что именно выходит из строя

Ошибки прав доступа при импорте означают, что идентификатор пользователя контейнера не имеет прав на запись в папку библиотеки. Сообщение выглядит как Access to the path ... is denied. Проверьте с помощью ls -ln /mnt/data/media, что идентификатор владельца совпадает с вашим PUID, и помните, что для входа в директории контейнеру необходим бит выполнения.

Файлы, владельцем которых отображается root, означают, что контейнер запустился до того, как была создана директория на хосте, поэтому Docker создал её от имени root. Остановите стек, chown директорию и запустите его снова.

Если после удаления торрента из qBittorrent файл из библиотеки исчезает, значит, импорт был выполнен копированием, которое впоследствии было удалено, либо вы удалили сами данные, а не запись о торренте. При использовании настоящих жестких ссылок (hardlinks) удаление одного имени оставляет другое нетронутым, так как данные освобождаются только тогда, когда количество ссылок достигает нуля.

Ситуация, когда диск заполняется быстрее, чем объем добавленных медиафайлов, является наиболее затратной формой проблемы копирования. Выполните проверку stat, описанную выше, прежде чем покупать дополнительное место на диске.

Требования этого стека к VPS

Три приложения семейства arr не создают большой нагрузки. Они опрашивают indexer, записывают данные в небольшую базу SQLite и переименовывают файлы. Сервер с 2 GB RAM без проблем запускает все четыре контейнера. Нагрузка возникает по другим причинам. Download client активно использует дисковый ввод-вывод при работе с большими torrent, а media server, транскодирующий видео на том же сервере, загрузит CPU. Храните media на томе с высокой реальной пропускной способностью. Если сервер выполняет другие важные задачи, установите ограничение пропускной способности для download client. Ресурсы для этих задач планируйте отдельно. Не рассчитывайте, что свободного запаса достаточно: self-hosted рабочее пространство AFFiNE — это ещё четыре контейнера с базой данных, и на сервере с 2 GB оно занимает большую часть памяти. Не каждый дополнительный сервис требует столько ресурсов. Одноцелевой сервис, например self-hosted трекер тренировок openGym, без проблем работает на том же сервере, если настроить для него собственный TLS и заранее определить расположение файла базы данных, прежде чем доверить ему историю тренировок за год. Любой стек с веб-приложением, базой данных Postgres и фоновой очередью задач требует ресурсов, близких к верхней границе этого диапазона, поэтому заранее решите, размещать ли self-hosted службу поддержки Chatwoot на этом сервере или на отдельном. Не ждите, пока ограничение проявится в середине импорта. Для ресурсоёмких периодических задач нужна ещё большая осторожность: при импорте важна пиковая, а не средняя нагрузка. Если вы рассматриваете self-hosted OneCLI, который выдаёт каждому пользователю собственного изолированного агента, сравните опубликованные требования к ресурсам с фактически доступными ресурсами при максимальной загрузке qBittorrent, а не со значением, которое показывает free -h на неиспользуемом сервере.

FAQ

Почему Sonarr копирует файлы вместо создания жестких ссылок?

Потому что с точки зрения контейнера источник и назначение находятся в разных файловых системах. Два отдельных bind-монтирования, такие как /downloads и /tv, воспринимаются как разные файловые системы, даже если они ведут на один и тот же диск хоста. Смонтируйте единый родительский каталог как /data в каждом контейнере, разместите внутри него папки с загрузками и библиотекой, и создание ссылок станет возможным. Проверьте результат с помощью stat -c '%i %h %n' для обоих файлов: у них должны быть одинаковый inode и счетчик ссылок 2.

Какие PUID и PGID следует использовать?

Используйте числовой идентификатор учетной записи хоста, которой принадлежит дерево медиафайлов; его можно узнать с помощью id -u и id -g. На чистом Ubuntu VPS это обычно 1000 для обоих параметров. Все контейнеры в стеке должны использовать одну и ту же пару, иначе одно приложение будет создавать файлы, которые другое не сможет изменить. После изменения значений пересоздайте контейнеры командой docker compose up -d --force-recreate и исправьте права на существующие файлы с помощью chown -R.

Нужно ли открывать эти веб-интерфейсы в Интернет?

Нет, этого делать не следует. Привяжите каждый опубликованный порт к 127.0.0.1 в файле Compose, а затем обращайтесь к интерфейсам через SSH-туннель, VPN или reverse proxy, который выполняет TLS (transport layer security) termination и добавляет собственную аутентификацию. Прямая публикация опаснее, чем кажется, так как Docker добавляет свои правила в firewall, и правило ufw deny не сможет остановить этот трафик.

Где найти пароль от qBittorrent?

Образ LinuxServer.io выводит временный пароль для пользователя admin в лог запуска. Выполните docker compose logs qbittorrent | grep -i password, чтобы прочитать его, а затем установите постоянный пароль в разделе Options и Web UI. Новый временный пароль генерируется при каждом перезапуске, пока вы не установите свой собственный.

Может ли Jellyfin использовать те же папки?

Да, именно в этом заключается смысл такой структуры. Смонтируйте /mnt/data/media в ваш медиасервер как /media, тогда его библиотеки будут доступны по путям /media/Movies и /media/Shows, в то время как Sonarr и Radarr будут записывать данные в те же каталоги через /data/media. Укажите для медиасервера те же значения PUID и PGID, чтобы он мог читать файлы, созданные стеком arr.

#sonarr#radarr#prowlarr#docker-compose#self-hosting