MAATRIX / Блог / Сколько RAM нужно для Dify

Сколько RAM нужно для Dify

Сколько RAM нужно для Dify

MAATRIX

В документации Dify стоит одна цифра — 2 vCPU и 4 ГБ RAM, — и она сбивает с толку: на четырёх гигабайтах стек поднимается, но живёт до первой серьёзной базы знаний. Память тратит не «Dify», а десяток контейнеров с разными аппетитами, и без потолка растёт из них только один. Ниже — разбор по компонентам, расчёт векторного индекса, замер на своей машине и переменные .env, которые снимают гигабайт.

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →

Короткий ответ: сколько памяти закладывать

Требования к Dify определяет не число пользователей, а объём базы знаний и количество плагинов. Диалоги — самая дешёвая часть системы: их считает внешняя модель, Dify только перекладывает байты.

СценарийRAMvCPUЧто ломается ступенью ниже
1–2 приложения на облачном API, база знаний до сотни документов4 ГБ2на 2 ГБ контейнеры стартуют, но первая индексация убивает worker
Отдел 10–30 человек, база в несколько тысяч фрагментов, 3–5 плагинов8 ГБ4на 4 ГБ индексация конкурирует с диалогами и выигрывает
База в сотни тысяч фрагментов, гибридный поиск, несколько воркеров16 ГБ4–8на 8 ГБ векторное хранилище занимает половину машины
Локальная модель на той же машине: эмбеддинги или 7–8B в Q416–32 ГБ8+на 8 ГБ стек и модель не уживаются

Главное: сам Dify — около двух гигабайт в покое, и эта величина почти не меняется. Всё, что сверху, — векторный индекс, пики индексации и плагины. И поправка, экономящая деньги: Dify не грузит PyTorch и не считает эмбеддинги сам — ни sentence-transformers, ни локального реранкера в контейнерах нет, векторизацию выполняет подключённый плагином провайдер.

Куда уходят гигабайты: разбор по контейнерам

Комплектный docker-compose.yaml поднимает девять-десять сервисов, и «Dify съел всю память» почти всегда значит, что съел один конкретный. Порядок величин в покое:

КонтейнерЧто этоПорядок резидентаОт чего растёт
apiFlask под gunicorn с воркерами gevent450–700 МБSERVER_WORKER_AMOUNT
workerтот же образ в режиме Celery400–900 МБиндексация, CELERY_WORKER_AMOUNT
webNext.js под PM2250–400 МБPM2_INSTANCES, по умолчанию 2
dbPostgreSQL 15-alpine150–400 МБPOSTGRES_SHARED_BUFFERS, число соединений
weaviateвекторное хранилище по умолчаниюот 200 МБ и без потолкаразмер базы знаний
plugin_daemonдемон плюс процесс на каждый плагин120 МБ + 60–150 МБ за плагинчисло плагинов
redisброкер Celery и кэш10–60 МБдлина очереди задач
sandboxGo-песочница для узла Code20–60 МБпараллельные запуски кода
ssrf_proxySquid для узлов HTTP Request40–120 МБcache_mem в конфиге Squid
nginxединственный публичный порт5–20 МБпрактически не растёт

Цифры — порядок, а не эталон: версия и набор плагинов двигают их процентов на двадцать. Три вещи из таблицы обычно удивляют.

  • api и worker — два экземпляра одного приложения. Оба грузят Flask, SQLAlchemy, Celery, SDK провайдеров и парсеры документов, и платить приходится дважды: без воркера не работает индексация.
  • Демон плагинов множится. В ветке 1.x провайдеры и инструменты вынесены в плагины, и plugin_daemon запускает каждый отдельным процессом со своим venv в /app/storage/cwd. Семь плагинов — семь процессов по сотне мегабайт и гигабайты окружений на диске.
  • web запускается дважды. PM2_INSTANCES равна 2: Next.js держит два процесса Node. Для пары десятков пользователей второй не нужен, а стоит 150–200 МБ.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Dify

Как измерить свой расход, а не поверить чужой таблице

Dify ставится в подкаталог docker, поэтому имя проекта Compose — docker, а контейнеры зовутся docker-api-1, docker-worker-1.

cd ~/dify/docker
docker compose ps --format 'table {{.Service}}\t{{.Status}}'
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}'

Вывод второй команды — форма, а не эталон, цифры у вас будут свои:

NAME                     MEM USAGE / LIMIT     MEM %
docker-api-1             612MiB / 3.822GiB     15.64%
docker-worker-1          548MiB / 3.822GiB     14.00%
docker-web-1             318MiB / 3.822GiB     8.13%
docker-weaviate-1        241MiB / 3.822GiB     6.16%
docker-db-1              196MiB / 3.822GiB     5.01%
docker-plugin_daemon-1   173MiB / 3.822GiB     4.42%
docker-ssrf_proxy-1       58MiB / 3.822GiB     1.48%
docker-sandbox-1          21MiB / 3.822GiB     0.54%
docker-redis-1            12MiB / 3.822GiB     0.31%
docker-nginx-1             9MiB / 3.822GiB     0.23%

Ловушка: на cgroup v2 docker stats показывает memory.current вместе со страничным кешем. Настоящую память процесса даёт строка anon, пик — отдельный счётчик ядра:

docker exec docker-worker-1 grep -E '^(anon|file|slab) ' /sys/fs/cgroup/memory.stat
CID=$(docker inspect -f '{{.Id}}' docker-worker-1)
cat /sys/fs/cgroup/system.slice/docker-$CID.scope/memory.peak

memory.peak появился в ядре 5.19 — на Ubuntu 24.04 (ядро 6.8) и Debian 12 он есть; обнуляется записью echo reset, чтобы померить одну операцию. Если стек уже падал, диагноз ставится по всем контейнерам сразу:

docker inspect -f '{{.Name}} OOMKilled={{.State.OOMKilled}} Exit={{.State.ExitCode}}' $(docker compose ps -aq)
dmesg -T | grep -i 'killed process'

Exit=137 и OOMKilled=true означают, что процесс убил не баг, а ядро. В логах авария выглядит по-разному. Celery в docker compose logs worker:

[ERROR/MainProcess] Process 'ForkPoolWorker-2' pid:41 exited with 'signal 9 (SIGKILL)'
billiard.exceptions.WorkerLostError: Worker exited prematurely: signal 9 (SIGKILL) Job: 0.

Gunicorn в docker compose logs api — прямым текстом:

[1] [ERROR] Worker (pid:19) was sent SIGKILL! Perhaps out of memory?

Смерть воркера Celery оставляет документ навсегда в статусе обработки, gunicorn — обрывает ответ пользователю. Общие симптомы — в разборе нехватки RAM.

Векторный индекс: единственная строка, которая растёт без потолка

Всё выше — константа. Векторное хранилище — переменная, и считать её надо заранее: переезжать потом дорого. Weaviate из комплекта держит HNSW-граф в оперативной памяти, и объём считается как «число фрагментов × размерность вектора × 4 байта» (float32) плюс 15–25 % на граф связей.

Фрагментов в базе1024 измерения1536 измерений3072 измерения
10 000~40 МБ~60 МБ~120 МБ
100 000~400 МБ~600 МБ~1,2 ГБ
500 000~2 ГБ~3 ГБ~6 ГБ
1 000 000~4 ГБ~6 ГБ~12 ГБ

Размерность задаёт модель эмбеддингов: у text-embedding-3-small это 1536, у text-embedding-3-large — 3072, у bge-m3 — 1024. «Большая» модель удваивает расход памяти, качество поиска по документации — обычно нет.

В автоматическом режиме Dify режет документ по абзацам с целевой длиной порядка пятисот токенов; в пользовательском предел держит INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH (по умолчанию 1000). Плотная страница A4 — 500–700 токенов, то есть фрагмент на страницу: сто тысяч фрагментов — порядка восьмидесяти тысяч страниц. Типичная корпоративная база — тысячи, максимум десятки тысяч фрагментов, то есть сотни мегабайт.

Два способа не платить за это памятью — и множитель, о котором забывают:

  • Режим индексации «Экономичный» предлагается при создании базы знаний рядом с «Высоким качеством». Он не вызывает модель эмбеддингов вообще: строит инвертированный индекс по ключевым словам и векторное хранилище не занимает. Минус такой же полный: это поиск по словам, а не по смыслу — «как оформить отпуск» не найдёт «ежегодный отдых».
  • Другое хранилище. VECTOR_STORE переключает бэкенд: qdrant держит векторы на диске в mmap, pgvector укладывает всё в тот же Postgres. Оба меняют память на задержку поиска. Важно: смена VECTOR_STORE не переносит данные — старые базы придётся переиндексировать целиком, заново оплатив эмбеддинги. Решать надо до заливки документов.
  • Множитель сверху — режим «родительский-дочерний»: он хранит и мелкие фрагменты, и крупные родительские, записей втрое-впятеро больше.

Пики: индексация, воркеры и плагины

Ровное потребление серверы не убивает — убивают пики. У Dify их четыре.

Индексация документов — самый прожорливый момент системы. Воркер читает файл в память целиком, режет на фрагменты, батчами шлёт их модели эмбеддингов и пишет результат в Postgres и векторную базу сразу. Пачка из полусотни документов на четырёх гигабайтах — верный способ получить Exit=137 у worker. Ограничители на входе: UPLOAD_FILE_SIZE_LIMIT (15 МБ) и UPLOAD_FILE_BATCH_LIMIT (5 файлов за раз). За ними стоит ещё NGINX_CLIENT_MAX_BODY_SIZE, иначе вместо ошибки памяти вы получите 413.

ETL_TYPE=Unstructured — движок разбора, который включают ради сложных PDF и таблиц. Он поднимает отдельный контейнер: плюс гигабайт-полтора к постоянному расходу.

Воркеры. Рядом с SERVER_WORKER_AMOUNT в .env.example стоит комментарий про формулу «2 × число ядер + 1». Для лёгких приложений она верна, здесь разрушительна: каждый воркер gunicorn — полная копия приложения на 400–600 МБ, а класс gevent асинхронный, один процесс держит сотни соединений. То же с CELERY_WORKER_AMOUNT.

Плагины висят постоянно, а не поднимаются по запросу. Здесь же отложенная мина: PLUGIN_PYTHON_ENV_INIT_TIMEOUT (120 секунд) — время на создание venv при установке. На забитой памятью машине плагин не «ставится медленно», а падает по таймауту — в интерфейсе это выглядит как «плагин не устанавливается».

Значения Postgres в .env рассчитаны на машину побольше. POSTGRES_EFFECTIVE_CACHE_SIZE=4096MB памяти не выделяет, но на сервере с 4 ГБ даёт планировщику неверную подсказку, а POSTGRES_MAX_CONNECTIONS=100 с POSTGRES_WORK_MEM=4MB — это потенциальные 400 МБ.

Как ужать Dify: .env, лимиты и swap

Сначала правки в docker/.env, которые ничего не ломают:

PM2_INSTANCES=1
SERVER_WORKER_AMOUNT=1
CELERY_WORKER_AMOUNT=1
CELERY_AUTO_SCALE=false
POSTGRES_MAX_CONNECTIONS=60
POSTGRES_SHARED_BUFFERS=192MB
POSTGRES_WORK_MEM=4MB
POSTGRES_MAINTENANCE_WORK_MEM=128MB
POSTGRES_EFFECTIVE_CACHE_SIZE=2048MB
UPLOAD_FILE_BATCH_LIMIT=3
ETL_TYPE=dify

PM2_INSTANCES=1 — самая безболезненная экономия: 150–200 МБ за строку. Дальше удалите неиспользуемые плагины. Переменные перечитываются только при пересоздании контейнеров: docker compose restart оставит старое окружение, и покажется, что правка не сработала.

cd ~/dify/docker && docker compose down && docker compose up -d

Второй слой — ограничители. Файл docker-compose.override.yaml рядом с основным подхватывается автоматически, а git pull его не трогает — в репозитории его нет:

services:
  weaviate:
    environment:
      GOMEMLIMIT: 1GiB
    mem_limit: 1400m
  worker:
    mem_limit: 1500m
  api:
    mem_limit: 1200m

GOMEMLIMIT — мягкий предел для сборщика мусора Go: Weaviate собирает мусор агрессивнее, а не растёт до упора. mem_limit — предохранитель, и о нём надо честно: он не уменьшает потребление, он локализует смерть. Без лимитов ядро выбирает жертву по oom_score и способно убить Postgres или sshd вместо воркера.

Третий слой — swap: на четырёх гигабайтах это не опция, а обязательный элемент:

fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
sysctl -w vm.swappiness=10

Десятка вместо дефолтных 60 значит, что ядро полезет в swap, только когда припёрло. Swap гасит пик индексации, но если туда уезжает Weaviate или Postgres, поиск по базе из миллисекунд превращается в секунды. Чего делать не стоит: отключать sandbox и ssrf_proxy ради полутора сотен мегабайт — первый изолирует выполнение кода из workflow, второй не пускает узел HTTP Request во внутреннюю сеть и в метаданные облака.

Какой сервер под Dify брать в MAATRIX

Конфигурация выводится из двух чисел: базового расхода стека (около 2 ГБ) и размера индекса по формуле из четвёртого раздела. Остальное — запас на пики.

Честный минимум: 2 vCPU, 4 ГБ RAM, 40–50 ГБ NVMe. Ровно документационный порог: консоль работает, два-три приложения на облачных моделях отвечают нормально, база в сотни документов индексируется. Ограничения называю прямо: swap обязателен, PM2_INSTANCES=1 обязателен, ETL_TYPE=Unstructured недоступен, плагинов — минимум, документы заливать порциями. Конфигурация для пилота, а не для растущей базы.

Комфортный вариант: 4 vCPU, 8 ГБ RAM, 80–100 ГБ NVMe. Считать мегабайты уже не нужно: два гигабайта стека, до гигабайта индекса, полдесятка плагинов, пик индексации — и всё ещё свободная память. Если база планируется на сотни тысяч фрагментов или нужен гибридный поиск с реранкером — сразу 16 ГБ. Диск считайте кратно объёму документов: образы, оригиналы в storage, Postgres, индекс, venv плагинов и логи. Двадцати гигабайт мало даже на старте.

Локация — Великобритания, Лондон. К памяти это отношения не имеет, но определяет, будет ли Dify работать вообще: с российского адреса маркетплейс плагинов не открывается, а OpenAI, Anthropic и Google отвечают отказом по региону — провайдер смотрит на исходящий IP, перевыпуск ключа не помогает. Лондон снимает обе проблемы и ближе к пользователям в Европе и России, чем Нью-Йорк. США берут ради американского IP, Франция — равноценная альтернатива, Россия оправдана только под 152-ФЗ с российскими моделями.

Dify есть в каталоге apps.maatrix.io и разворачивается автоматически при заказе сервера — вручную ставить Docker и разбираться с compose-файлом не нужно. Работает на Ubuntu и Debian. Адрес консоли и стартовые доступы появятся в личном кабинете, в разделе «Доступ». Оплата — картой российского банка, по СБП, криптовалютой или токеном MAAT: иностранная карта не нужна, хотя сервер стоит в Лондоне. Пошаговая настройка — в установке Dify на VPS, разбор аварий — в частых ошибках Dify, сравнение — в Dify против Flowise.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Dify

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →

Частые вопросы

Хватит ли 2 ГБ RAM для Dify?

Нет. Стек стартует и первые полчаса выглядит рабочим, но базовое потребление около двух гигабайт — на них уже нет места ни системе, ни Docker. Первая загрузка документа даёт Exit=137 у контейнера worker и WorkerLostError: Worker exited prematurely: signal 9 (SIGKILL) в логах.

Weaviate занимает три гигабайта и не отдаёт память после удаления базы знаний. Это утечка?

Обычно нет: удалённые объекты помечаются надгробиями и вычищаются из HNSW-графа асинхронно, а память, освобождённую сборщиком Go, процесс не всегда сразу возвращает системе. Если через сутки цифра не изменилась, помогает docker compose restart weaviate.

Поднял SERVER_WORKER_AMOUNT до 9 по формуле «2 × CPU + 1», и сервер стал падать. Почему?

Каждый воркер gunicorn — полная копия приложения на 400–600 МБ, девять копий это 4–5 ГБ только под api. Формула написана для синхронных воркеров, а Dify использует gevent. Начинайте с 1 и поднимайте до 2–3, только когда упирается процессор, а не память.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.