Dify на Ubuntu 24.04: пошаговая установка
Инструкция из репозитория Dify помещается в три команды, и на чистой Ubuntu 24.04 они честно отрабатывают — ровно до момента, когда стек упирается в то, чем noble отличается от 22.04: подкачки нет, docker.io идёт без плагина compose, резолвер висит на 127.0.0.53, а sshd перезапускается сокетом. Ниже — путь от свежей noble до рабочей консоли Dify на своём домене под HTTPS: тексты ошибок, конфиги и честный расчёт памяти.
Содержание
- Что ставим и что даёт noble
- Подготовка системы: обновление, sshd на сокете и фаервол
- Память и sysctl: swap, overcommit и max_map_count
- Docker Engine и Compose v2 в noble
- Стек Dify: клон, генерация секретов, первый запуск
- Домен, HTTPS на nginx 1.24 и жизнь после перезагрузки
- Какой сервер под Dify заказать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество 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 | Что из этого следует |
|---|---|---|
| Python | 3.12.3 | системный pip закрыт политикой PEP 668 |
| nginx | 1.24.0 | директива http2 on; не поддерживается |
| iptables | 1.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 — десятки моделей в одном окне. Оплата картой РФ и по СБП.