MAATRIX / Блог / Dify на Ubuntu 24.04: пошаговая установка

Dify на Ubuntu 24.04: пошаговая установка

Dify на Ubuntu 24.04: пошаговая установка

MAATRIX

Инструкция из репозитория Dify помещается в три команды, и на чистой Ubuntu 24.04 они честно отрабатывают — ровно до момента, когда стек упирается в то, чем noble отличается от 22.04: подкачки нет, docker.io идёт без плагина compose, резолвер висит на 127.0.0.53, а sshd перезапускается сокетом. Ниже — путь от свежей noble до рабочей консоли Dify на своём домене под HTTPS: тексты ошибок, конфиги и честный расчёт памяти.

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

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

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

Что ставим и что даёт noble

Dify — платформа для сборки чат-ботов, агентов и workflow с базой знаний, а на сервере — связка примерно из десяти контейнеров: api, Celery-воркер, фронтенд, PostgreSQL, Redis, векторное хранилище, песочница для узла Code, демон плагинов и внутренний nginx. Кто за что отвечает — в установке Dify на VPS.

Ограничение, которое экономит день: Dify не считает модели. Инференс всегда снаружи, видеокарта платформе не нужна. А память нужна: документация просит не меньше 2 vCPU и 4 ГБ RAM, и это порог выживания, а не рекомендация.

Первая команда на новой машине — инвентаризация:

lsb_release -ds; uname -r; python3 -V; dpkg --print-architecture
nproc; free -h | awk '/Mem|Swap/{print $1, $2}'; df -h --output=avail /

Типовой ответ noble: Ubuntu 24.04.x LTS, ядро 6.8.0-…-generic, Python 3.12.3, архитектура amd64. Две строки важны. Swap 0B — подкачки нет, чиним в третьем разделе. amd64 — часть образов стека собрана не под все архитектуры, и на ARM docker compose up останавливается на no matching manifest for linux/arm64.

КомпонентВерсия в 24.04Что из этого следует
Python3.12.3системный pip закрыт политикой PEP 668
nginx1.24.0директива http2 on; не поддерживается
iptables1.8.10 (nf_tables)Docker пишет правила мимо ufw

Подготовка системы: обновление, sshd на сокете и фаервол

Первый сюрприз noble — needrestart: он вклинивается в apt upgrade диалогом «Which services should be restarted?» и в скрипте вешает установку намертво.

NEEDRESTART_MODE=a apt-get update && NEEDRESTART_MODE=a apt-get -y upgrade
sed -i "s/^#\?\$nrconf{restart}.*/\$nrconf{restart} = 'a';/" /etc/needrestart/needrestart.conf
apt-get install -y git curl ca-certificates ufw

Третья строка не лишняя: минимальные облачные образы идут без git, и клонирование обрывается подсказкой Command 'git' not found.

Дальше SSH. В noble sshd работает через сокет-активацию: слушает не демон, а юнит ssh.socket, поэтому правка Port в /etc/ssh/sshd_config не даёт ничего — после systemctl restart ssh вы по-прежнему видите 22-й порт.

systemctl edit ssh.socket
# [Socket]
# ListenStream=
# ListenStream=2222
systemctl daemon-reload && systemctl restart ssh.socket

Пустая строка ListenStream= обязательна: без неё новый порт добавится к старому. Второй капкан — довески: sshd_config начинается с Include /etc/ssh/sshd_config.d/*.conf, cloud-init кладёт туда 50-cloud-init.conf, а в sshd побеждает первое встреченное значение, так что ваш PasswordAuthentication no в конце файла молча проигрывает. Проверяйте итог разбора: sshd -T | grep -E '^(port|passwordauthentication)'.

Фаервол пускает внутрь только SSH и веб. Порты 5001, 3000, 5432, 6379, 8194 и 5002 наружу не нужны никогда — контейнеры Dify общаются по внутренней сети Docker:

ufw default deny incoming && ufw default allow outgoing
ufw allow 22/tcp && ufw allow 80,443/tcp && ufw enable

И честная оговорка, обесценивающая эти строки. В 24.04 iptables — шим над nftables (iptables -V отвечает iptables v1.8.10 (nf_tables)), а правила публикации портов Docker пишет в цепочки DOCKER и DOCKER-USER, срабатывающие раньше ufw: опубликованный порт торчит в интернет при активном ufw status. Лечится не правилом, а отказом от публикации на все адреса — файлом docker-compose.override.yaml (нужен Compose v2.24.4 и новее с тегом !override):

services:
  nginx:
    ports: !override
      - "127.0.0.1:8080:80"

Без тега списки портов в Compose складываются, а не заменяются. Результат проверяют снаружи: в выводе nmap -Pn -p 22,80,443,5001,5432,8080 адрес_сервера открытыми могут быть только 22, 80 и 443. Базовые правила — в статье про фаервол UFW.

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

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

Развернуть Dify

Память и sysctl: swap, overcommit и max_map_count

Облачные образы Ubuntu идут без подкачкиswapon --show не выводит ни строки. Для десяти контейнеров это принципиально: пик даёт worker, когда разбирает PDF и гоняет чанки через модель эмбеддингов, и на 4 ГБ без swap он кончается убитым процессом.

fallocate -l 4G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

Честно про цену: swap спасает от OOM-killer, но не ускоряет — уехала индексация в подкачку, и документ обрабатывается заметно дольше. Это страховка, а не замена памяти.

cat > /etc/sysctl.d/99-dify.conf <<'EOF'
vm.swappiness = 10
vm.overcommit_memory = 1
vm.max_map_count = 262144
net.core.somaxconn = 1024
EOF
sysctl --system

vm.overcommit_memory = 1 — требование Redis, а не наша фантазия. Без него контейнер redis при каждом старте пишет в лог:

WARNING Memory overcommit must be enabled! Without it, a background save or
replication may fail under low memory condition.

Смысл практический: под нехваткой памяти Redis не сможет сделать форк для фонового сохранения — вы рискуете очередью Celery, той самой, что ведёт индексацию документов.

vm.max_map_count нужен только при переходе на Elasticsearch или Milvus: иначе контейнер падает с max virtual memory areas vm.max_map_count [65530] is too low. И мелочь при разборе OOM: Ubuntu держит kernel.dmesg_restrict = 1, поэтому без sudo вы получите dmesg: read kernel buffer failed: Operation not permitted.

Docker Engine и Compose v2 в noble

Соблазн выполнить apt install docker.io большой, но посмотрите apt-cache policy docker.io: ветка в noble отстаёт на несколько мажорных версий и главное — идёт без плагина compose. Добить недостающее через pip не выйдет, в 24.04 включена политика PEP 668:

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you want to install.

Это защита системного Python, и обходить её флагом --break-system-packages ради compose бессмысленно. Ставим из официального репозитория:

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" > /etc/apt/sources.list.d/docker.list
apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker --version && docker compose version

Кодовое имя в строке репозитория — noble; в руководствах под 22.04 стоит jammy, и apt update отвечает ошибкой про отсутствующий Release-файл. Если docker compose version пишет docker: 'compose' is not a docker command, поставился docker.io, и Dify на нём не соберётся.

Демон настраиваем руками. Драйвер json-file по умолчанию пишет логи без ограничений, а api и worker разговорчивы: за пару месяцев /var/lib/docker/containers съедает десятки гигабайт, Postgres на полном диске уходит в read-only, и консоль отдаёт 500 на любое сохранение. Файл /etc/docker/daemon.json закрывает это и вопрос с DNS:

{
  "dns": ["1.1.1.1", "9.9.9.9"],
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

Резолвером в Ubuntu служит systemd-resolved на 127.0.0.53, а внутри контейнера этот адрес не значит ничего. Docker честно предупреждает — строка видна в journalctl -u docker:

Local ("127.0.0.53") DNS resolver found in resolv.conf and containers can't
use it. Using default external servers: [nameserver 8.8.8.8 nameserver 8.8.4.4]

Подмена работает, пока публичные DNS доступны с сервера. Зарезан исходящий 53-й порт — plugin_daemon не достучится до маркетплейса и вернёт ошибку вида lookup marketplace.dify.ai … i/o timeout, а выглядеть это будет как пустой каталог плагинов.

И про безопасность: группа docker равносильна root — через монтирование хостовой файловой системы в контейнер. А если пойдёте путём rootless-Docker, учтите ограничение AppArmor на непривилегированные пространства имён (sysctl kernel.apparmor_restrict_unprivileged_userns отвечает 1): для rootlesskit понадобится отдельный профиль.

Стек Dify: клон, генерация секретов, первый запуск

Репозиторий кладём в /opt, compose-файл живёт в подкаталоге docker/, и все команды выполняются оттуда: в файле есть монтирования относительными путями, и запуск из другого места создаст пустые каталоги вместо конфигов.

git clone https://github.com/langgenius/dify.git /opt/dify
cd /opt/dify && git tag -l | tail -5
git checkout <нужный_тег> && cd /opt/dify/docker

Тег фиксируйте осознанно: main — ветка разработки, и переход на неё прогонит необратимые миграции базы.

Дальше порядок действий важнее самих команд. В .env.example лежат общеизвестные дефолты: пароль базы difyai123456, тот же у Redis, ключ песочницы dify-sandbox. Менять их надо до первого запуска: образ PostgreSQL применяет POSTGRES_PASSWORD только при инициализации пустого каталога данных, а после старта пароль меняется уже через ALTER USER.

cd /opt/dify/docker && cp .env.example .env
RPW=$(openssl rand -hex 24); SBX=$(openssl rand -hex 24)
sed -i \
  -e "s|^SECRET_KEY=.*|SECRET_KEY=$(openssl rand -base64 42)|" \
  -e "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -hex 24)|" \
  -e "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$RPW|" \
  -e "s|^CELERY_BROKER_URL=.*|CELERY_BROKER_URL=redis://:$RPW@redis:6379/1|" \
  -e "s|^SANDBOX_API_KEY=.*|SANDBOX_API_KEY=$SBX|" \
  -e "s|^CODE_EXECUTION_API_KEY=.*|CODE_EXECUTION_API_KEY=$SBX|" \
  -e "s|^EXPOSE_NGINX_PORT=.*|EXPOSE_NGINX_PORT=8080|" \
  .env
grep -E '^(SECRET_KEY|REDIS_PASSWORD|CELERY_BROKER_URL)=' .env

Две пары значений связаны, и на них спотыкаются чаще всего. REDIS_PASSWORD входит в строку брокера CELERY_BROKER_URL — поменяете пароль, забыв про строку, и получите worker в статусе Up, не берущий ни одной задачи: документы навсегда останутся «в очереди». SANDBOX_API_KEY обязан совпадать с CODE_EXECUTION_API_KEY, иначе узел Code отваливается по авторизации.

SECRET_KEY — мастер-ключ установки: им подписываются сессии и шифруются API-ключи провайдеров в базе. Скопируйте его в менеджер паролей сразу — потеряете, и все подключённые модели превратятся в нерасшифровываемый мусор.

docker compose up -d
docker compose ps --format "table {{.Service}}\t{{.Status}}"
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/

Первый up -d тянет несколько гигабайт образов. Ждать надо не «зелёного» вывода compose, а Up у всех сервисов и (healthy) у db и redis: контейнер api на старте прогоняет миграции и до их завершения отвечает пятисотыми. Если в выводе Restarting или Exited (137) — разбор в частых ошибках Dify.

Последний шаг перед доменом — мастер на /install, где создаётся владелец рабочего пространства. Страница открыта любому, кто первым найдёт адрес, поэтому либо закройте её переменной INIT_PASSWORD, либо зайдите сразу.

Домен, HTTPS на nginx 1.24 и жизнь после перезагрузки

Заводим A-запись и ждём, пока dig +short dify.example.com вернёт адрес сервера. Дальше два пути: отдать TLS внутреннему nginx стека (профиль certbot и переменная CERTBOT_DOMAIN) или поставить перед ним системный nginx. На Ubuntu второй путь удобнее: сертификаты обновляет штатный таймер.

Ловушка noble: в репозитории лежит nginx/1.24.0, а директива http2 on; появилась в 1.25.1 — свежие мануалы её используют, и конфиг падает с nginx: [emerg] unknown directive "http2". На 1.24 пишите listen 443 ssl http2;. Файл /etc/nginx/sites-available/dify:

server {
    listen 80;
    server_name dify.example.com;

    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "";
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Четыре строки здесь не косметические. X-Forwarded-Proto обязателен: без него Dify собирает ссылки по схеме входящего запроса, и браузер режет их с Mixed Content. proxy_buffering off включает потоковую выдачу — ответ модели идёт по SSE, и с буферизацией пользователь смотрит в пустой экран, а потом получает всю простыню разом. Таймауты в 3600 секунд закрывают длинные workflow: на стандартных 60 прилетает 504 Gateway Time-out ровно на минуте. client_max_body_size поднимают в паре с UPLOAD_FILE_SIZE_LIMIT в .env, иначе вы просто смените текст ошибки 413.

ln -s /etc/nginx/sites-available/dify /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
certbot --nginx -d dify.example.com
systemctl status certbot.timer --no-pager

nginx -t должен ответить syntax is ok и test is successful; certbot сам допишет секцию с 443-м портом и редирект, а таймер обновления проверяйте сразу, а не через восемьдесят дней.

Остаётся пережить перезагрузку. systemctl is-enabled docker отвечает enabled из коробки, а у сервисов Dify стоит restart: always, так что стек поднимается сам. Хуже с unattended-upgrades, включённым в 24.04 по умолчанию: обновление библиотек дёргает перезапуск сервисов, и если это случится посреди индексации, документ останется в статусе ошибки. Управляйте моментом — оставьте Unattended-Upgrade::Automatic-Reboot "false"; в /etc/apt/apt.conf.d/50unattended-upgrades. Бэкап заводите до появления данных: дамп Postgres, каталог volumes/app/storage и обязательно .env — без SECRET_KEY дамп бесполезен.

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

Считаем по составу стека: одновременно работают PostgreSQL, Redis, векторное хранилище, Node-фронтенд, два Python-процесса и демон плагинов. Требование разработчиков — от 2 vCPU и 4 ГБ RAM.

Честный минимум: 2 vCPU, 4 ГБ RAM, 40 ГБ NVMe. Ровно документационный порог: стек соберётся, консоль откроется, один-два бота на внешнем API будут работать. Чего не будет — запаса на индексацию: когда worker разбирает крупный PDF, а база и векторное хранилище пишут параллельно, память кончается, и OOM-killer выбирает самый жирный процесс. Файл подкачки здесь не опция. Диск 40 ГБ тоже впритык: одни образы занимают несколько гигабайт.

Комфортный вариант: 4 vCPU, 8 ГБ RAM, 80 ГБ NVMe. Конфигурация, на которой платформой пользуется команда: несколько приложений, база знаний на тысячи документов, полдесятка плагинов. Разница ощущается не в чате — там всё равно ждём внешнюю модель, — а в разборе документов: парсинг и нарезка на чанки распараллеливаются по ядрам. Планируете локальную модель рядом — считайте честно: 8 ГБ на Dify плюс около 4,5 ГБ весов модели 7B в Q4 плюс контекст дают уже 16 ГБ, и практичнее вынести инференс на отдельную машину.

Локация — Великобритания, Лондон. Причина техническая. Dify 1.x регулярно ходит наружу за пакетами плагинов в маркетплейс и за ответами модели, и с российского адреса ломается и то и другое: OpenAI отвечает unsupported_country_region_territory, Google — User location is not supported for the API use., Anthropic — сухой 403. Перевыпуск ключа не помогает, провайдер смотрит на исходящий IP. С британской площадки всё отвечает штатно, а до пользователей в Европе и России путь короче, чем через Атлантику. США (Нью-Йорк) берут, когда нужен американский адрес и чистый IP. Россия оправдана только под 152-ФЗ с российскими моделями — тогда за плагинами придётся ходить через прокси.

Всё описанное выше руками повторять не обязательно. Dify есть в каталоге приложений apps.maatrix.io: приложение ставится автоматически при заказе сервера, работает на Ubuntu и Debian, а доступы — адрес консоли и стартовые ключи — появляются в личном кабинете, в разделе «Доступ». Оплата картами российских банков, по СБП, криптовалютой или токеном MAAT; иностранная карта не нужна, хотя сервер стоит в Лондоне. После первого входа заведите владельца на /install и сохраните SECRET_KEY. Дальше — к делу: например, к сборке ИИ-ассистента для команды.

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

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

Развернуть Dify

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

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

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

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

Обязательно ли именно Ubuntu 24.04, или подойдёт 22.04 либо Debian?

Dify требует не дистрибутив, а Docker Engine с Compose v2, так что подойдёт и 22.04, и Debian 12. В 24.04 удобнее срок поддержки до 2029 года; расплата — описанные выше особенности noble. Если берёте 22.04, поменяйте noble на jammy в строке репозитория Docker, иначе apt update не найдёт Release-файл.

Хватит ли 2 ГБ RAM, если сделать большой swap?

Нет. Стек стартует, консоль откроется, но первая же индексация уводит worker в подкачку целиком, и вы получаете либо получасовую обработку одного файла, либо Exited (137) — тот же OOM.

Нужно ли открывать в ufw порты 5001, 3000 или 5432?

Нет: контейнеры общаются по внутренней сети Docker, наружу должен смотреть только веб. Помните и обратное — ufw deny не защищает опубликованный контейнером порт: привязывайте публикацию к 127.0.0.1 через docker-compose.override.yaml и проверяйте результат снаружи через nmap.

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

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