Полностью автоматический self-hosted музыкальный стек: стриминг как в Spotify, авто-пополнение библиотеки из торрентов и Soulseek в lossless качестве, персональная статистика прослушиваний, обложки/тексты/метадата — всё по умолчанию.
Navidrome (стриминг) <— Lidarr (менеджер коллекции) <— qBittorrent + Soulseek
(источники), Koito + Maloja (статистика), всё в Docker Compose. Из коробки:
только настоящий FLAC, обложки и синхро-тексты в каждом треке, древо путей
Артист/Альбом (Год)/, раздача библиотеки обратно в сообщество.
Кратко (полная пошаговая установка с созданием каталогов — в разделе «Запуск стека», выполняй именно его, иначе будут ошибки прав):
git clone https://github.com/gifi71/pwnd-music.git /opt/pwnd-music
cd /opt/pwnd-music
cp .env.example .env && nano .env # пути, таймзона, пароли, ключи
# ... создать каталоги config/ и data/ (см. «Запуск стека») ...
docker compose up -d # первый раз СОБИРАЕТ кастомные
# образы локально (~неск. минут)Первый up собирает 4 кастомных образа (enrichment, fakeflac, *-provision) —
это нормально. Если у тебя GHCR-пакеты сделаны публичными,
вместо сборки можно docker compose pull.
Затем ~10 минут пост-настройки (ниже): ключи Koito/Maloja, Last.fm/Spotify, трекеры.
| Сервис | Порт | Назначение |
|---|---|---|
| Navidrome | 4533 | Стриминг, веб-плеер, Subsonic API (мобильные клиенты: Symfonium, Tempo, play:Sub) |
| Koito ×N | 4110, 4111… | Персональная статистика прослушиваний — свой инстанс на юзера (Koito однопользовательский) |
| multi-scrobbler | 9078 | Роутер скробблов: по токену раскидывает прослушивания юзеров в их Koito |
| Maloja | 42010 | Углублённая статистика/анализ прослушиваний (пишется параллельно с Koito) |
| Lidarr | 8686 | Менеджер коллекции: следит за артистами, ищет релизы |
| Prowlarr | 9696 | Менеджер торрент-индексеров (RuTracker, NNM-Club), сам прописывает их в Lidarr |
| qBittorrent | 8090 | Торрент-клиент для Lidarr |
| slskd | 5030 | Soulseek-демон + веб-UI для ручного поиска |
| Soularr | 8265 | Мост: wanted-список Lidarr → автопоиск в Soulseek через slskd |
| lidarr-provision | — | Одноразовая настройка Lidarr через API (качество/метадата/нейминг/обложки) |
| aurral | 3001 | Discovery + request-UI: находит новое под вкус → Lidarr, «flow»-плейлисты в Navidrome |
| enrichment | — | Ночной cron: обложки + синхро-лирика (каскад) + ReplayGain-теги (rsgain) |
| fakeflac | — | Ночной cron: детектит фейковый (перекодированный) FLAC, шлёт отчёт |
| beets | — | Ночной cron: НЕразрушающий чек целостности + дубликатов, шлёт отчёт |
flowchart TD
U([ты: добавил артиста/альбом]) --> L[Lidarr]
P[Prowlarr] -. "индексеры трекеров" .-> L
L -- "wanted-список" --> S[Soularr]
L -- "грабит релиз с трекера" --> Q[qBittorrent]
S -- "API" --> SL[slskd]
SL <-- "поиск / скачивание" --> SS([сеть Soulseek])
SL --> D1["/data/downloads/slskd"]
Q --> D2["/data/downloads/torrents"]
D1 --> IMP[Lidarr: импорт hardlink]
D2 --> IMP
IMP --> M["/data/music"]
M -. "раздача библиотеки (seeding)" .-> SL
Q -. "сидинг торрентов" .-> SS2([торрент-пиры])
EN[enrichment ⏰: вшить обложки + синхро-лирику] --> M
M --> EN
M --> FF[fakeflac ⏰: детект фейкового FLAC]
FF --> TG([отчёт в Telegram])
M --> N[Navidrome: сканирует и стримит]
N -- "трек доигран → ListenBrainz API" --> MS[multi-scrobbler]
MS -- "токен gifi" --> K1[koito-gifi]
MS -- "токен al" --> K2[koito-al]
MS -- "токен gifi" --> MJ[maloja]
lidarr-provisionиprowlarr-provision— одноразовые init-контейнеры: приupнастраивают Lidarr (качество/метадата/нейминг/обложки/root folder), генерят конфиг Soularr и связку Prowlarr→Lidarr, затем завершаются. На схеме не показаны (это настройка, не поток данных).⏰— ночной cron.
${DATA_DIR}/ # большой диск (по умолчанию /srv/media)
├── music/ # библиотека
└── downloads/
├── torrents/ # загрузки qBittorrent
└── slskd/
├── complete/ # завершённые загрузки slskd
└── incomplete/
репозиторий/
├── docker-compose.yml
├── .env # секреты (НЕ в гите)
├── ms-config/config.json # роутинг multi-scrobbler (секреты — через env)
├── templates/soularr-config.ini
└── config/ # runtime-данные контейнеров (НЕ в гите)
Единый корень ${DATA_DIR} смонтирован в Lidarr как /data целиком: загрузки и
библиотека на одной файловой системе, поэтому импорт — мгновенный hardlink без
удвоения занятого места.
Подойдёт любой хост с Docker: VM, LXC-контейнер, отдельная машина или NAS. Ориентир:
- Docker + Docker Compose (
curl -fsSL https://get.docker.com | sh). - CPU/RAM: 2 vCPU / 4 ГБ (хватает; enrichment/fakeflac-кроны прожорливее в момент ночного прогона — librosa/onnx).
- Диск: небольшой системный (≈20 ГБ) + отдельный большой том под музыку,
смонтированный в
${DATA_DIR}(по умолчанию/srv/media). Загрузки и библиотека должны быть на одной ФС — иначе импорт копирует, а не хардлинкает (удвоение места, см. ниже).
На Proxmox это обычно LXC (Debian 12, unprivileged, в Options включить
keyctl=1,nesting=1для Docker) или VM; том с музыкой пробрасывается mountpoint'ом/диском в${DATA_DIR}. Конкретный способ проброса — на твоё усмотрение, стек к нему безразличен.
git clone https://github.com/gifi71/pwnd-music.git /opt/pwnd-music && cd /opt/pwnd-music
# 1. Окружение
cp .env.example .env
nano .env # пути, таймзона, логин Soulseek, пароли, API-ключ slskd
# 2. Подхватить переменные из .env для следующих шагов
set -a; . ./.env; set +a
# 3. Каталоги конфигов. ВАЖНО: создать заранее — иначе docker создаст их
# под root, а сервисы бегут под PUID и не смогут писать.
# (config.ini Soularr НЕ копируем — его генерит lidarr-provision сам.)
mkdir -p config/{navidrome,koito-gifi,koito-al,maloja,multi-scrobbler,lidarr,prowlarr,qbittorrent,slskd,soularr,fakeflac-reports,aurral,beets,beets-reports}
# 3a. Пресид API-ключа Maloja (чтобы не ходить за ним в UI)
echo "default: $MLJ_API_KEY" > config/maloja/apikeys.yml
sudo chown -R "$PUID:$PGID" config
# 4. Каталоги данных (+ _aurral для плейлистов discovery)
sudo mkdir -p "$DATA_DIR"/music/_aurral "$DATA_DIR"/downloads/torrents \
"$DATA_DIR"/downloads/slskd/{complete,incomplete}
# 4a. Smart-плейлисты (.nsp) — копируем в библиотеку (Navidrome их подхватит)
cp smart-playlists/*.nsp "$DATA_DIR"/music/
sudo chown -R "$PUID:$PGID" "$DATA_DIR"
# 5. Поехали (первый раз собирает кастомные образы ~неск. минут)
docker compose up -dПри up контейнеры lidarr-provision и prowlarr-provision сами настроят
Lidarr (качество, метадата, нейминг, обложки, root folder /data/music),
сгенерят config.ini Soularr и свяжут Prowlarr→Lidarr — руками не надо.
У каждого юзера свой Koito; multi-scrobbler различает юзеров по токену
(LB_TOKEN_* из .env). Кто статистику не хочет — просто не включает
скробблинг у себя, остальное его не касается.
Подключение юзера (пример — gifi, для al аналогично со своими значениями):
- Открыть Navidrome
http://<host>:4533— при первом входе создаётся админ. - Открыть Koito юзера
http://<host>:4110(al —:4111). Визарда нет — аккаунт создан автоматически при первом старте (логин/пароль —KOITO_USERNAME/KOITO_PASSWORDиз.env). Нажать Sign In. - Settings → API Keys → скопировать ключ → вписать в
.envвKOITO_API_KEY_GIFI→docker compose up -d multi-scrobbler. - В Navidrome под юзером gifi: Settings → Personal → включить
Scrobble to ListenBrainz → вставить токен
LB_TOKEN_GIFIиз.env→ Save. - Проверка: послушать трек до конца → появится в его Koito.
Статус роутера:
http://<host>:9078.
- Открыть
http://<host>:8686, задать аутентификацию.
Всё остальное Lidarr настраивает контейнер lidarr-provision автоматически
(см. «Что Lidarr настраивает сам») —
русский язык, профили, нейминг, обложки, root folder, конфиг Soularr. Руками
ничего вписывать не нужно.
- Логин qBittorrent —
admin, пароль при первом старте временный, смотреть:docker logs qbittorrent→ строка "temporary password". - Открыть
http://<host>:8090, сменить пароль: Tools → Options → Web UI. - Там же Downloads → Default Save Path:
/data/downloads/torrents, и Behavior → Language → Русский. - В Lidarr: Settings → Download Clients →
+→ qBittorrent: Hostqbittorrent, Port8090(=QBIT_WEBUI_PORT), логин/пароль из шага 2, Categorymusic.
config.ini Soularr генерится автоматически (lidarr-provision подставляет
ключи Lidarr и slskd). Делать ничего не нужно — проверка:
docker logs soularr без ошибок подключения, веб-морда http://<host>:8265.
slskd доступен на http://<host>:5030 (логин из .env) — там же ручной поиск
по Soulseek, если хочется качнуть что-то мимо Lidarr.
- Открыть
http://<host>:9696, задать аутентификацию. - Если трекеры/метадата блокируются: Settings → Indexers →
+Proxy → Socks5 (хост/порт/креды своего прокси), Tag:proxy. - Indexers →
+→ найти NoNameClub (или RuTracker) → логин/пароль аккаунта трекера → при необходимости Tagproxy→ Test → Save.
Связка Prowlarr → Lidarr создаётся автоматически контейнером
prowlarr-provision при docker compose up (App «Lidarr», fullSync). Каждый
добавленный индексер сам появляется в Lidarr — Settings → Apps руками настраивать
не нужно. (Если хочешь проверить: Prowlarr → Settings → Apps → там уже есть Lidarr.)
aurral (http://<host>:3001) находит новую музыку под твой вкус, добавляет в
Lidarr и публикует «flow»-плейлисты в Navidrome. Интеграции — в его UI (не env):
- Открыть
:3001, в настройках вписать Lidarr (http://lidarr:8686+ API key), Navidrome/Subsonic (http://navidrome:4533+ логин/пароль), опц. Last.fm и ListenBrainz (источники истории для рекомендаций). - Плейлисты aurral пишутся в
${DATA_DIR}/music/_aurral→ Navidrome их видит.
Smart-плейлисты (.nsp) из smart-playlists/ уже скопированы в библиотеку при
установке — Navidrome строит их сам по правилам (недавнее, давно не слушал,
любимое, топ-рейтинг). Правь файлы под себя.
docker-compose.yml: скопировать блокkoito-gifi→koito-<имя>, заменить имя контейнера, том (config/koito-<имя>) и порт..env: добавитьLB_TOKEN_<ИМЯ>(openssl rand -hex 16),KOITO_API_KEY_<ИМЯ>=changeme,KOITO_<ИМЯ>_PORT.- В compose у
multi-scrobblerпробросить обе новые переменные вenvironment. ms-config/config.json: добавить source и client по образцу существующих.docker compose up -d, затем шаги 2–4 из «Персонального скробблинга» (забрать API-ключ нового Koito, юзер вставляет свой токен в Navidrome).
Контейнер lidarr-provision при каждом docker compose up декларативно
применяет через API Lidarr перечисленное ниже. Это источник истины в коде —
правки через UI Lidarr будут перезаписаны на следующем up. Нужно поменять
дефолт — правь lidarr-provision/provision.py, а не UI.
| Настройка | Значение |
|---|---|
| Quality Profile | Lossless (perfectionist) — lossless, MP3-320 только fallback, cutoff=FLAC; остальные профили удаляются |
| Metadata Profile | тянуть всё (Album/EP/Single + все secondary) |
| Track Naming | {Артист}/{Альбом} (Год)/NN - Трек, rename ON |
| Metadata Consumer | Kodi/Emby — обложки артистов/альбомов на диск |
| Write Audio Tags | allFiles — тегать файлы метадатой MusicBrainz + вшивать обложку |
| Root Folder | /data/music |
| UI язык | русский (uiLanguage=11, env LIDARR_UI_LANGUAGE для override) |
| UI дата/время | понедельник, DD.MM.YYYY, HH:mm (РФ) |
Soularr config.ini |
генерится из шаблона + ключи Lidarr/slskd |
Аналогично prowlarr-provision создаёт связку Prowlarr→Lidarr (индексеры
синкаются сами).
Стек настроен на максимум качества «из коробки». Что работает автоматически:
- lidarr-provision — одноразовый контейнер, при каждом
upдекларативно применяет в Lidarr через API: профиль качества (только lossless, MP3-320 лишь как fallback когда FLAC вообще нет), Metadata Profile «тянуть всё» (альбомы + синглы + EP + все вторичные типы), раскладкуАртист/Альбом (Год)/NN - Трек, запись обложек артистов/альбомов на диск (Kodi-консьюмер). Читает API-ключ Lidarr из егоconfig.xmlсам. - enrichment (ночной cron) — вшивает обложку из
cover.jpgв каждый трек без встроенной картинки; синхро-лирику каскадом (LRCLIB → Musixmatch → NetEase → Genius — больше покрытие, особенно RU) в теги +.lrcрядом; и пишет ReplayGain/R128 теги (rsgain) — Navidrome нормализует громкость, нет скачков между треками. - fakeflac (ночной cron) — детектит фейковый (перекодированный из mp3) FLAC (ML-модель FLAD), шлёт отчёт в Telegram. Ничего не удаляет — решение за тобой.
- beets (ночной cron) — НЕразрушающий чек библиотеки: целостность файлов
(
badfiles) + дубликаты, отчёт в Telegram. Файлы не трогает. - Smart-плейлисты (
.nsp) — Navidrome строит авто-обновляемые плейлисты по правилам (недавнее, давно-не-слушал, любимое, топ-рейтинг). Примеры вsmart-playlists/, копируются в библиотеку при установке. - aurral — discovery: находит новое под вкус, добавляет в Lidarr, публикует «flow»-плейлисты в Navidrome (см. пост-настройку).
Метадата артистов (фото/био) в Navidrome требует ключей в .env:
ND_LASTFM_APIKEY/SECRET (last.fm/api)
и ND_SPOTIFY_ID/SECRET (developer.spotify.com).
Без них страницы артистов будут без фото.
*enrichment / fakeflac / beets / -provision — кастомные образы: CI собирает
их в GHCR (docker compose pull), либо локально docker compose build.
fakeflac тянет ML-модель FLAD. Не нужны — закомментируй сервисы в compose.
Maloja (:42010) пишется параллельно с Koito (multi-scrobbler fan-out) и даёт
более глубокую статистику/экспорт. Ключ: Maloja UI → Admin → API Keys →
создать → в .env MLJ_API_KEY → docker compose up -d multi-scrobbler.
Профиль качества Lidarr настроен на lossless с cutoff на FLAC: берётся лучший
доступный релиз; если есть только MP3 — возьмётся MP3-320, а когда позже
появится FLAC — Lidarr сам апгрейдит. Защиты от фейкового FLAC в Lidarr нет
— этим занимается контейнер fakeflac.
Как правильно удалить плохой релиз, чтобы Lidarr не скачал то же самое:
- Lidarr → артист → альбом → у файла Delete (удалит с диска).
- ВАЖНО: Lidarr → Activity → History → найти этот релиз → Blocklist (или при удалении из очереди поставить галку Blocklist Release). Без блок-листа автопоиск возьмёт ровно тот же битый релиз снова.
- Lidarr пере-ищет альбом и возьмёт следующий по качеству. Soularr дополнительно
ведёт свой denylist (
failed_import_denylist = True) для slskd-загрузок.
Подключаются через Prowlarr (раздел пост-настройки выше). С открытой регистрацией и хорошим lossless:
- RuTracker — лучший источник lossless, нативный индексер в Prowlarr, открытая регистрация. Нюанс: сам блокирует РФ-IP → прокси должен быть не-РФ и со стабильным (sticky) IP, иначе логин-куки рвутся.
- NoNaMe Club (NNM-Club) — хороший вторичный, открытая регистрация.
- Rutor — без регистрации.
- RED / Orpheus — лучшие в мире, но только по инвайтам.
Задача владельца: зарегистрироваться на трекере, добавить его в Prowlarr
(Indexers → +), при необходимости повесить tag прокси, затем Settings → Apps
синхронизирует индексер в Lidarr.
Lidarr берёт метадату из MusicBrainz. Если артист не находится:
- Проверь поиском на самом musicbrainz.org или в MusicBrainz Picard (десктоп-теггер с lookup), Harmony (поиск релизов). Самохостимого полного MB-UI нет — база слишком большая.
- Если артиста реально нет в MusicBrainz — добавь его там (регистрация бесплатна); правка появится в Lidarr после рефреша кэша (обычно часы-сутки).
- Либо скачай вручную (slskd UI) и сделай Manual Import в Lidarr (Wanted → Manual Import → путь к файлам). Рут-фолдер загрузок добавлять НЕ надо — это плодит дубли.
Чтобы раздавать обратно сообществу (важно!) и не насиловать домашний аплинк —
настройки в config/qbittorrent/qBittorrent/qBittorrent.conf. qBittorrent
переписывает conf при выходе, поэтому правь при остановленном контейнере:
docker compose stop qbittorrent
# отредактировать conf, секция [BitTorrent]:
# Session\GlobalMaxRatio=-1 ; сид без лимита по ratio
# Session\GlobalMaxSeedingMinutes=-1 ; сид без лимита по времени
# Session\MaxRatioAction=0 ; 0=Pause (НИКОГДА не 3=delete!)
# Session\GlobalDLSpeedLimit=0 ; скачивание безлимит
# Session\GlobalUPSpeedLimit=1500 ; отдача KiB/s (~75% аплинка)
docker compose start qbittorrentИмпорт в Lidarr — hardlink, поэтому раздача не рвётся после импорта (файл
тот же inode). slskd раздаёт библиотеку автоматически (см. SLSKD_SHARED_DIR).
Часть внешних сервисов закрыта от РФ-IP (Cloudflare отдаёт 403):
api.lidarr.audio (метадата/поиск Lidarr) и indexers.prowlarr.com
(определения индексеров Prowlarr), плюс трекеры. Лечение — SOCKS5/HTTP-прокси
с не-РФ выходом:
.env→PROWLARR_PROXYиLIDARR_PROXY(можно один и тот же,socks5://user:pass@host:port). Весь внешний трафик этих сервисов пойдёт через прокси, внутренние сервисы исключены (NO_PROXYв compose).- Переменные опциональны: пусто = стек работает без прокси (актуально вне РФ).
- Для трекеров в Prowlarr — отдельный Indexer Proxy в его UI.
Navidrome умеет проигрывать веб-радиостанции: UI → Radios → добавить название
- прямой URL потока (mp3/aac, не страницу плеера). Доступно и в мобильных клиентах (Symfonium, play:Sub, Amperfy). Где брать станции и прямые ссылки:
- Radio Browser — открытая community-база, бесплатно, без аккаунта, тысячи станций всех стран включая РФ (фильтр по country). Даёт готовый прямой stream-URL — копируй в Navidrome.
- SomaFM — куратор, без рекламы, хорошее качество (есть 320k AAC): Groove Salad, Drone Zone и др.
Алгоритмического «радио по треку» (Spotify-style) у Navidrome нет — это либо
Smart Playlists (.nsp-правила), либо клиент с Last.fm-похожими (у нас Last.fm
включён).
- В Lidarr добавить артиста с парой альбомов → Search.
- Смотреть: qBittorrent тянет с трекеров, Soularr (
docker logs -f soularr) ищет недостающее в Soulseek. - После импорта файлы появляются в
/srv/media/music, Navidrome подхватывает при сканировании (каждый час, либо вручную: Activity → Quick Scan). - Послушать трек до конца → он появляется в персональном Koito юзера (если тот включил скробблинг).
Версии образов зафиксированы в docker-compose.yml. Обновление — через
Dependabot
(конфиг .github/dependabot.yml): раз в неделю открывает PR с bump'ом
версий (minor/patch — сгруппированы, major — отдельно), ты ревьюишь и
мержишь. Включается само при пуше на GitHub. После мержа на сервере:
git pull && docker compose pull && docker compose up -d
docker compose logs -f <service> # логиНюанс linuxserver-образов (lidarr, qbittorrent): они пересобираются на свежей
базе и под тем же тегом (CVE-фиксы базового слоя без смены версии приложения).
Dependabot такие ребилды не видит, поэтому раз в месяц стоит делать
docker compose pull && docker compose up -d даже без открытых PR.
Четыре образа собираются из этого репо: enrichment, fakeflac,
lidarr-provision, prowlarr-provision. GitHub Actions
(build-images.yml) собирает их и пушит в
GHCR (ghcr.io/gifi71/pwnd-music-*) при пуше в main, по тегам v* и
вручную. На сервере они тянутся обычным docker compose pull — локальная
сборка не нужна. Если правишь Dockerfile/скрипты — git push, CI пересоберёт,
на сервере docker compose pull && up -d. Хочешь собрать локально —
docker compose build.
Бэкапить: .env, config/ (базы и настройки сервисов), сам ${DATA_DIR}/music.
Репозиторий хранит всю декларативную конфигурацию; восстановление сервера =
clone + .env из бэкапа + config/ из бэкапа + docker compose up -d.
Стек рассчитан на доступ из локальной сети. Наружу (интернет) ничего не
пробрасывать как есть — для удалённого доступа использовать VPN до дома
(WireGuard/Tailscale) или reverse-proxy с аутентификацией. Торренты при
необходимости можно завернуть в VPN, добавив контейнер
Gluetun и network_mode: service:gluetun
у qBittorrent.
Telegram-уведомления (отчёты fakeflac/enrichment) — опциональны. Бот:
@BotFather → /newbot → токен в TELEGRAM_BOT_TOKEN.
chat_id: написать боту, открыть
https://api.telegram.org/bot<TOKEN>/getUpdates → взять chat.id в
TELEGRAM_CHAT_ID. Пусто = уведомления выключены.
Этот проект — набор свободного программного обеспечения с открытым исходным кодом (Navidrome, Lidarr и др.) для развёртывания личного домашнего медиа-сервера. Программное обеспечение является нейтральным инструментом: оно не содержит и не распространяет какой-либо защищённый авторским правом контент.
Проект предназначен исключительно для личного и домашнего использования с контентом, на использование которого у вас есть законное право (собственные записи, легально приобретённые фонограммы, произведения под свободными лицензиями или в общественном достоянии).
Согласно ст. 1273 ГК РФ, гражданин вправе без согласия правообладателя и без выплаты вознаграждения воспроизводить правомерно обнародованное произведение исключительно в личных целях. Эта норма не распространяется на распространение (раздачу, доведение до всеобщего сведения) произведений — такие действия могут влечь гражданскую, административную или, при крупном размере (свыше 100 000 ₽), уголовную ответственность по ст. 146 УК РФ.
Ответственность за законность любого контента, который вы загружаете, храните, воспроизводите или раздаёте с помощью этого ПО, а также за соблюдение законодательства вашей юрисдикции, несёте исключительно вы. Авторы и участники проекта не несут ответственности за то, как вы используете эти инструменты.
Данный текст не является юридической консультацией. Программное обеспечение предоставляется «как есть», без каких-либо гарантий.