Сколько RAM нужно для Funkwhale
Funkwhale часто выбирают как федеративную альтернативу SoundCloud — сервис, где можно не только слушать свою музыку, но и подписываться на чужие библиотеки и каналы по протоколу ActivityPub, как в Mastodon. Проблема в том, что многие подходят к расчёту сервера так же, как для Navidrome или другого лёгкого аудио-плеера, и упираются в нехватку памяти уже на этапе установки. Funkwhale — не один бинарник, а полноценное веб-приложение на Django с PostgreSQL, Redis и очередью фоновых задач Celery, и федерация добавляет постоянную фоновую нагрузку, которой нет у изолированных серверов. Разберём, из чего складывается память Funkwhale и сколько реально закладывать под разные сценарии.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Из чего складывается память Funkwhale
Архитектурно Funkwhale ближе к Nextcloud или Mastodon, чем к Navidrome, — это несколько взаимодействующих процессов, а не один компактный бинарник:
- API-сервер на Django + Gunicorn. Обрабатывает HTTP-запросы, REST и GraphQL API, аутентификацию. Gunicorn поднимает несколько воркер-процессов (число обычно считают по формуле
2 × ядра + 1), и каждый — отдельный процесс Python с собственным потреблением памяти, обычно от 80 до 150 МБ в покое в зависимости от версии зависимостей. - PostgreSQL — обязательная СУБД. В отличие от Navidrome с встроенной SQLite, Funkwhale требует полноценный PostgreSQL-сервер: треки, альбомы, исполнители, подписки, федеративные объекты (Follow, Like, Announce) и очередь активностей хранятся в реляционной базе. Даже небольшой инстанс держит в памяти буферы
shared_buffersи кеш планов запросов — фиксированный «налог», которого у SQLite-решений просто нет. - Redis — кеш и брокер очередей. Выполняет две роли: кеширует часто запрашиваемые данные (метаданные треков, сессии) и служит брокером сообщений для Celery — через него API ставит фоновые задачи, а воркер их забирает.
- Celery-воркер — вся тяжёлая и фоновая работа. Импорт библиотеки, генерация превью обложек, обработка входящей и исходящей федерации, уведомления — всё это выполняется асинхронно в отдельном процессе (или нескольких, в зависимости от параметра
concurrency). Именно он чаще всего даёт непредсказуемые пики. - nginx — обратный прокси и раздача статики. Отдаёт собранный фронтенд (статические файлы Vue.js) и проксирует запросы к API и медиафайлам. Сам по себе лёгкий, но именно через него идёт стриминг аудио.
Сложите базовые состояния этих процессов — и станет понятно, почему Funkwhale в покое ощутимо тяжелее Navidrome даже без единого слушателя: там, где у Navidrome работает один Go-процесс с SQLite, у Funkwhale минимум пять взаимодействующих сервисов, у каждого свой минимальный «вес».
Федерация ActivityPub — постоянный фон, а не разовый пик
Главное архитектурное отличие Funkwhale от локальных медиасерверов — федерация не выключается никогда, пока инстанс на кого-то подписан или на него подписаны. Это меняет саму природу нагрузки: вместо разового пика при сканировании библиотеки появляется ровный фоновый расход, зависящий от активности сети инстансов, а не от действий ваших собственных пользователей.
Что конкретно нагружает Celery-воркер и Redis из-за федерации:
- Входящие активности. Каждый раз, когда пользователь на другом инстансе лайкает трек, публикует альбом в подписанной вами библиотеке или комментирует — приходит подписанный HTTP-запрос, который нужно проверить (HTTP Signatures), распарсить JSON-LD и сохранить как объект в PostgreSQL.
- Исходящие активности. Ваши действия (подписки, лайки, публикации) рассылаются подписчикам — при большом числе подписчиков на других инстансах это веерная рассылка HTTP-запросов через Celery.
- Проксирование удалённого медиа. Обложки альбомов, аватары и иногда сами аудиофайлы с удалённых инстансов кешируются локально через медиапрокси — это не только диск, но и временные всплески памяти при скачивании.
- Число подписок и подписчиков — главный множитель. Инстанс, подписанный на десяток библиотек, почти не заметит федеративной нагрузки. Инстанс с активным сообществом, подписанный на сотни каналов, держит Celery-воркер в постоянной лёгкой загрузке даже ночью.
Практический вывод: если вы поднимаете Funkwhale только для себя и не собираетесь подписываться на чужие библиотеки (федерацию можно отключить в настройках инстанса), вы фактически убираете самый непредсказуемый источник фоновой нагрузки и получаете профиль памяти, близкий к обычному self-hosted веб-приложению на Django.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверИмпорт библиотеки и транскодирование
Первая загрузка коллекции у Funkwhale устроена иначе, чем у Navidrome: сканирование не блокирует основной процесс, а ставится в очередь Celery пакетами задач — по одному треку или пачке треков на задачу, в зависимости от версии.
- Импорт идёт через очередь, а не напрямую. API-процесс при запуске импорта создаёт задачи в Redis, Celery-воркер разбирает их по очереди (или параллельно, если
concurrencyбольше 1), читает теги, извлекает обложки и пишет метаданные в PostgreSQL. Это не блокирует веб-интерфейс, но означает, что при большом импорте воркер держит повышенное потребление памяти долгое время, а не коротким пиком. - Транскодирование по требованию. Если клиент запрашивает поток не в исходном формате или битрейте, Funkwhale на лету вызывает внешний процесс
ffmpeg. По памяти это сопоставимо с тем же механизмом у Navidrome — обычно несколько десятков мегабайт на активный поток, — но у Funkwhale транскодирование конкурирует за ресурсы с уже занятыми Django, PostgreSQL, Redis и Celery, а не с единственным лёгким бинарником. - Параллелизм воркера — прямой рычаг памяти. Параметр
concurrencyCelery определяет, сколько задач обрабатывается одновременно. Выше параллелизм — быстрее импорт, но и выше пиковое потребление памяти воркера; на серверах с 1–2 ГБ RAM разумно держать его в 1–2, а не задавать по числу ядер.
На практике для библиотеки в несколько тысяч треков полный импорт занимает от получаса до нескольких часов в зависимости от параллелизма и плотности метаданных, и всё это время Celery-воркер работает не в покое — это стоит учитывать при выборе тарифа, если планируете загружать библиотеку сразу целиком.
Сколько RAM закладывать по сценарию использования
Ориентировочные цифры для инстанса на Docker с PostgreSQL, Redis и одним Celery-воркером на Ubuntu 24.04. Это ориентир для выбора тарифа, а не гарантированный потолок — точные значения зависят от версии Funkwhale, числа Gunicorn-воркеров, параллелизма Celery и активности подписанных инстансов.
| Сценарий | RAM в покое | RAM при импорте библиотеки | RAM с федерацией и стримингом |
|---|---|---|---|
| Личный инстанс, федерация выключена, 1 пользователь | 900 МБ – 1,3 ГБ | +300–500 МБ | — |
| Личный инстанс с федерацией, несколько подписок | 1,1–1,5 ГБ | +300–500 МБ | +150–300 МБ фоном |
| Небольшой инстанс на семью/друзей, 3–10 пользователей | 1,3–1,8 ГБ | +400–700 МБ | +300–500 МБ |
| Инстанс сообщества, активная федерация, десятки подписчиков | 1,8–2,5 ГБ | +500 МБ–1 ГБ | +500 МБ–1 ГБ и выше |
Нижняя граница почти во всех сценариях уже выше, чем у Navidrome с запасом, — это стоимость обязательной СУБД и очереди задач, а не признак неправильной настройки. Для личного нефедеративного инстанса имеет смысл закладывать сервер с 2 ГБ RAM, для активной федерации и нескольких пользователей — от 4 ГБ, с запасом на пики импорта и на PostgreSQL, которому тоже стоит выделить не менее 256–512 МБ под shared_buffers.
Docker-compose с лимитами памяти
Упрощённая конфигурация с явными лимитами на каждый сервис: без них один процесс при пике (например, воркер во время импорта) может забрать память у PostgreSQL или Redis и уронить весь инстанс целиком.
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: funkwhale
POSTGRES_USER: funkwhale
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- "./data/postgres:/var/lib/postgresql/data"
deploy:
resources:
limits:
memory: 512m
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- "./data/redis:/data"
deploy:
resources:
limits:
memory: 128m
api:
image: funkwhale/api:latest
restart: unless-stopped
env_file: .env
depends_on:
- postgres
- redis
volumes:
- "./data/music:/music:ro"
- "./data/media:/app/media"
- "./data/static:/app/staticfiles"
deploy:
resources:
limits:
memory: 512m
celeryworker:
image: funkwhale/api:latest
restart: unless-stopped
command: celery -A funkwhale_api.taskapp worker -l info --concurrency=1
env_file: .env
depends_on:
- postgres
- redis
volumes:
- "./data/music:/music:ro"
- "./data/media:/app/media"
deploy:
resources:
limits:
memory: 512m
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
volumes:
- "./nginx.conf:/etc/nginx/conf.d/default.conf:ro"
- "./data/media:/protected/media:ro"
- "./data/static:/protected/staticfiles:ro"
depends_on:
- api
deploy:
resources:
limits:
memory: 64m
Суммарно лимиты в этом примере дают около 1,7 ГБ жёсткого потолка — под личный инстанс с умеренной федерацией разумно ставить сервер с 2–4 ГБ RAM, чтобы лимиты были ограничителем на случай сбоя, а не постоянным потолком. --concurrency=1 у Celery-воркера намеренно консервативен: поднимать его стоит только если сервер уже показывает запас памяти при обычной нагрузке. Общие принципы работы с лимитами контейнеров разобраны в статье про ресурсы и лимиты CPU и памяти в Docker.
Funkwhale против Navidrome: федерация в обмен на память
Оба сервиса раздают личную музыкальную коллекцию по сети, но по архитектуре и требованиям к серверу это разные весовые категории.
| Funkwhale | Navidrome | |
|---|---|---|
| Федерация с другими серверами | Да, ActivityPub | Нет |
| База данных | PostgreSQL (обязательна) | Встроенная SQLite |
| Очередь фоновых задач | Celery + Redis | Нет, всё в одном процессе |
| Число процессов в поставке | 5+ (api, worker, postgres, redis, nginx) | 1 бинарник |
| Типичный минимум RAM | 1–1,5 ГБ и выше при федерации | 512 МБ – 1 ГБ |
| Возможность подписки на чужие библиотеки | Да, основная функция | Нет |
Разница не в том, что Funkwhale «сделан хуже» — федерация и полноценная СУБД с очередью задач нужны именно для того, чего у Navidrome нет в принципе: подписок на чужие каталоги, лайков и комментариев, общения между независимыми инстансами. Если нужен просто личный стриминг своей коллекции без соцсетевой составляющей, сколько RAM нужно для Navidrome — вопрос с заметно более скромным ответом, а разворачивается сервис по инструкции Navidrome в Docker Compose. Если интересна именно федеративная модель, у Funkwhale похожая логика на другие ActivityPub-проекты вроде PeerTube — с видео-аналогом её можно сравнить по статье PeerTube в Docker Compose, где федерация и очереди задач устроены концептуально так же, только для видео.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Хватит ли 1 ГБ RAM для Funkwhale?
Технически инстанс может запуститься и на 1 ГБ с отключённой федерацией и одним пользователем, но без запаса для импорта библиотеки и любых пиков это рискованно — PostgreSQL, Redis, API и Celery-воркер вместе почти выбирают этот объём уже в состоянии покоя. Комфортный минимум для личного использования — 2 ГБ.
Можно ли заменить PostgreSQL на SQLite, чтобы сэкономить память?
Нет, Funkwhale не поддерживает SQLite как основную СУБД — это архитектурное решение проекта, обусловленное сложностью схемы данных (федеративные объекты, полнотекстовый поиск, права доступа). Экономить на этом компоненте не получится, только закладывать его в расчёт заранее.
Растёт ли память с числом подписанных инстансов и подписчиков?
Да, и это едва ли не главный переменный фактор для Funkwhale — в отличие от локальных медиасерверов, где нагрузка зависит от размера собственной библиотеки, здесь фон создаёт активность целой сети инстансов, на которые вы подписаны или которые подписаны на вас.
Что произойдёт, если памяти не хватит Celery-воркеру во время импорта?
Процесс воркера будет остановлен OOM-killer'ом, задачи, которые он не успел обработать, останутся в очереди Redis и будут подхвачены заново после перезапуска контейнера (restart: unless-stopped) — обычно без потери данных, но с задержкой.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →