SSD Nodes Learn 8GB RAM — $66/год
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-01

Как запустить arr stack в одном Docker Compose

Запустите Prowlarr, Sonarr, Radarr и qBittorrent на VPS из одного Docker Compose. Общие PUID, PGID и пути к томам сохранят hardlinks при импорте.

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

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

Стек не выполняет поиск контента за вас. Prowlarr использует добавленные вами индексаторы, а выбор индексаторов и соблюдение требований законодательства остаются вашей ответственностью. В этом руководстве рассматриваются вспомогательные компоненты: пользователи, пути, разрешения, контейнерная сеть и проверки, подтверждающие работоспособность системы.

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

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

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

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

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

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

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

Контейнеры записывают файлы от имени пользователя с числовым идентификатором, заданным параметрами 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

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

Каталоги библиотеки намеренно называются 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 направили бы этот трафик напрямую в обход правила deny в ufw. Это поведение постоянно вызывает вопросы. Оно объясняется в статье почему 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. По состоянию на July 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. Ни одно из этих приложений не должно быть доступно из общедоступного интернета только через собственную страницу входа.

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

docker compose logs qbittorrent | grep -i password

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

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

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

В 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 передаёт определения индексаторов в другие приложения. Поэтому индексатор нужно настроить один раз, а не два. Для этого Prowlarr требуется API (программный интерфейс приложения) ключ от каждого приложения.

В Sonarr откройте Settings, затем General, и скопируйте API ключ. В Prowlarr откройте Settings, затем Apps, добавьте приложение Sonarr и заполните 3 поля. 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. Помните, что каталог должен иметь бит execute, чтобы контейнер мог войти в него.

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

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

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

Что этому стеку требуется от VPS

Три приложения arr нетребовательны к ресурсам. Они опрашивают индексаторы, записывают данные в небольшую базу SQLite и переименовывают файлы. Сервер с 2 GB оперативной памяти без проблем запускает все четыре контейнера. Нагрузка возникает по другим причинам. Клиент загрузки при работе с большими торрентами полностью загружает дисковый ввод-вывод, а медиасервер, транскодирующий видео на том же сервере, загружает процессор. Храните медиаданные на томе с высокой фактической пропускной способностью. Если сервер выполняет другие важные задачи, установите ограничение пропускной способности для клиента загрузки.

FAQ

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

Потому что с точки зрения контейнера источник и назначение находятся в разных файловых системах. Два отдельных bind mount, например /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 (защиту транспортного уровня) и добавляет собственную аутентификацию. Публикация интерфейсов напрямую опаснее, чем кажется, поскольку 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