Dokploy: деплой падает — причины и решение
Вы нажали Deploy, лог пробежал зелёным, а карточка приложения стала красной без объяснений. Или наоборот: деплой отмечен успешным, а домен отдаёт 502. Жалоба «dokploy деплой не работает» распадается на три разных сбоя — на клонировании, на сборке или в момент, когда Swarm запускает задачу. Ниже — как понять, какая стадия упала у вас, с точными текстами ошибок и командами.
Содержание
- Где именно падает деплой: три стадии и как их различить
- Стадия clone: ключи, ветка и вебхук, который не долетел
- Стадия build: exit code 137 — это память, а не ваш код
- Стадия deploy: образ собран, а Swarm не сводит сервис
- Деплой зелёный, а домен отдаёт 502 или 404
- Переменные окружения, которые ломают деплой молча
- Какой сервер под Dokploy взять в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Где именно падает деплой: три стадии и как их различить
Dokploy выкатывает в три шага: клонирует репозиторий в /etc/dokploy/applications/<имя>/code, собирает образ (Nixpacks, ваш Dockerfile, buildpacks или Compose) и отдаёт результат Docker Swarm, который сводит сервис к нужному числу реплик. Веб-лог показывает один сплошной поток, и настоящая ошибка уезжает вверх за строками установки пакетов, поэтому смотрите не только в браузер:
docker service ls --filter name=myapp
docker service ps myapp --no-trunc | head -5
docker service logs --since 10m myapp 2>&1 | tail -40
Главный индикатор — колонка REPLICAS: 1/1 значит, что задача запущена и живёт, 0/1 — Swarm либо не смог её разместить, либо она падает по кругу. Флаг --no-trunc обязателен: без него ошибка обрезается ровно там, где начинается смысл. Сборку выполняет сама панель (в неё проброшен /var/run/docker.sock), её логи — docker logs dokploy --since 15m, сохранённые логи деплоев лежат в /etc/dokploy/logs/.
| Что видно в логе | Стадия | Раздел |
|---|---|---|
Permission denied (publickey) | clone | 2 |
exit code: 137, no space left on device | build | 3 |
no suitable node, task: non-zero exit (1) | deploy | 4 |
| Деплой зелёный, браузер даёт 502 или 404 | после deploy | 5 |
Стадия clone: ключи, ветка и вебхук, который не долетел
Лог обрывается на первых строках — до сборки дело не дошло. Три типовых сюжета.
fatal: could not read Username for 'https://github.com': No such device or address — приватный репозиторий тянется по HTTPS без учётных данных, а спросить их интерактивно git в контейнере не может. Подключите провайдера в разделе Git панели или переведите источник на SSH.
git@github.com: Permission denied (publickey). — ключ есть, но репозиторий его не знает. Ключи создаются в разделе SSH Keys, публичную часть кладут в Settings → Deploy keys репозитория, а не в личный профиль. Проверка — ssh -i /etc/dokploy/ssh-keys/<имя>/id_rsa -T git@github.com, рабочий ответ начинается с Hi user/repo! You've successfully authenticated. Соседняя ошибка Host key verification failed. бывает со своим Gitea или GitLab на нестандартном порту: ssh-keyscan -p 2222 git.example.com >> ~/.ssh/known_hosts.
fatal: Remote branch develop not found in upstream origin — ветку переименовали в main, а в карточке приложения осталось старое имя. Рядом вторая мелочь: Build Path для монорепозитория. Сервис лежит в apps/web, а путь оставлен / — получите failed to read dockerfile: open Dockerfile: no such file or directory.
Если автодеплой не срабатывает вовсе, откройте Settings → Webhooks → Recent Deliveries. Код 200 значит, что виновата настройка приложения: выключен тумблер Autodeploy или ветка вебхука не та. Ошибка соединения или таймаут — панель недоступна снаружи: она слушает 3000, и трафик гит-платформы должен до неё доходить (ufw allow 3000/tcp, а лучше домен с HTTPS перед ней).
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть DokployСтадия build: exit code 137 — это память, а не ваш код
Самая частая причина, по которой Dokploy деплой не работает на исправном проекте:
#14 ERROR: process "/bin/sh -c npm run build" did not complete successfully: exit code: 137
137 — это 128 + 9, то есть SIGKILL: своей волей сборщик так не завершается, его убило ядро. Подтверждает dmesg -T | grep -iE 'out of memory|killed process' | tail -3:
[Wed Aug 26 03:11:42 2026] Out of memory: Killed process 44123 (node)
total-vm:5142204kB, anon-rss:2374112kB, file-rss:0kB, shmem-rss:0kB, UID:0
Соседний случай — FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory: это лимит кучи V8, а не память машины, лечится переменной NODE_OPTIONS=--max-old-space-size=3072. Выше физической памяти кучу поднимать бессмысленно: внятная ошибка Node сменится глухим exit code: 137.
Замеры пика RSS на 2 vCPU и Node 22: Vite с React на трёхстах модулях просит 0,8–1,0 ГБ, Next.js 15 среднего сайта — 2,2–2,6 ГБ, Nuxt 3 и Angular 18 — до 3 ГБ. Прибавьте сам Dokploy: панель с Postgres, Redis и Traefik в простое занимает около 330 МБ (сколько RAM нужно для Dokploy). Быстрая подушка — swap, которого на VPS обычно нет:
swapon --show # пусто — swap отсутствует
fallocate -l 4G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Честно про цену: swap спасает сборку от смерти, но не ускоряет её. Та же сборка Next.js на 4 ГБ занимала 3 минуты 40 секунд, на 2 ГБ со swap — около 11 минут. Это страховка, а не конфигурация для прода.
Вторая беда стадии build — диск: failed to prepare extraction snapshot: write /var/lib/docker/overlay2/8f3a.../diff: no space left on device.
$ docker system df # и df -i: иногда кончаются inode, а не байты
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 38 6 21.4GB 17.8GB (83%)
Build Cache 412 0 9.1GB 9.1GB
Через месяц работы 40-гигабайтный диск забивается слоями и кешем без единого «тяжёлого» приложения. Чистка — docker builder prune -af --filter until=168h и docker image prune -af. Но docker system prune -a на узле Swarm бездумно не запускайте: он снесёт образы, не занятые работающими сервисами, включая тот, на который вы собирались откатиться (откат в Dokploy — это запуск предыдущего тега образа). Остальные поломки панели — в статье частые ошибки Dokploy.
Стадия deploy: образ собран, а Swarm не сводит сервис
Сборка успешна, а приложение красное или бесконечно «деплоится» — вся правда в одной команде:
$ docker service ps myapp --no-trunc
NAME DESIRED STATE CURRENT STATE ERROR
myapp.1 Ready Pending 4 minutes ago "no suitable node (insufficient resources on 1 node)"
no suitable node (insufficient resources on 1 node). На вкладке Advanced в блоке Resources два разных поля. Memory Reservation — заявка, Swarm вычитает её из ёмкости узла ещё до запуска; Memory Limit — потолок контейнера. Вписали резервацию 4096 на машине с 4 ГБ — планировщик не найдёт места, и задача останется в Pending навсегда: деплой выглядит зависшим, хотя работает как задумано. Держите резервацию в пределах 256M–512M, ограничивайте лимитом.
task: non-zero exit (1). Образ запустился и умер сам, причина в логах приложения: docker service logs myapp --since 5m. Классика — старт раньше базы и ECONNREFUSED 10.0.1.7:5432 либо отсутствующая переменная. А exit (137) здесь значит другое, чем при сборке: контейнер вылез за Memory Limit.
Bind for 0.0.0.0:5432 failed: port is already allocated. Порт опубликован во вкладке Advanced → Ports, а он уже занят. Внутри одного сервера публиковать порты приложениям не нужно вовсе: они общаются через overlay-сеть по имени сервиса.
Обновление встало на полпути. Проверьте статус rolling update:
docker service inspect myapp --format '{{.UpdateStatus.State}}: {{.UpdateStatus.Message}}'
# paused: update paused due to failure or early termination of task
Так бывает, когда в Dockerfile есть HEALTHCHECK, а приложение поднимается дольше, чем даёт проверка: Swarm считает задачу неудачной и замирает, оставив работать старую версию. Лечится параметром start_period: 60s и повторным docker service update --force myapp.
Деплой зелёный, а домен отдаёт 502 или 404
Панель рапортует об успехе, docker service ls показывает 1/1, а браузер — нет: виноват маршрут, а не деплой. 404 page not found от Traefik значит, что роутера для домена нет:
docker service inspect myapp --format '{{json .Spec.Labels}}' | jq
docker logs dokploy-traefik --since 10m 2>&1 | tail -20
Меток traefik.http.routers.* нет — домен не добавлен во вкладке Domains либо добавлен другому приложению. Метки есть, а 404 остаётся — проверьте A-запись: dig +short app.example.com должен вернуть IP этого сервера, а не CDN.
502 Bad Gateway — роутер есть, бэкенд недостижим, и обе причины про порт. Первая: в карточке домена указан не тот Container Port, Dokploy его не угадывает. Вторая коварнее: приложение слушает 127.0.0.1 — внутри контейнера работает, из соседнего нет.
docker exec -it $(docker ps -q -f name=myapp) sh -c 'ss -ltnp || netstat -ltnp'
LISTEN 0 511 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=1,fd=21))
Адрес 127.0.0.1:3000 вместо 0.0.0.0:3000 — вот и весь 502. Правится в приложении: app.listen(3000, '0.0.0.0'), next start -H 0.0.0.0, vite preview --host 0.0.0.0, 0.0.0.0:8000 для Gunicorn и Uvicorn.
Третья причина — сеть. Traefik и приложения должны быть в одной overlay-сети dokploy-network. Созданные через панель попадают в неё сами, а при деплое своего docker-compose.yml сеть подключают руками — этот шаг забывают чаще прочих:
services:
web:
image: myapp:latest
networks: [dokploy-network]
networks:
dokploy-network:
external: true
И последнее: стоящий на хосте Nginx или Apache держит 80 и 443, Traefik не стартует, и 404 получают все приложения разом. Проверка — ss -ltnp | grep -E ':80 |:443 '.
Переменные окружения, которые ломают деплой молча
Здесь деплой обычно проходит успешно, а результат оказывается неправильным.
Build-time против runtime. Nixpacks отдаёт переменные и в сборку, а сборка по вашему Dockerfile — нет: туда значения приходят только как ARG. Отсюда классика — next build, падающий на prisma generate с Error: Environment variable not found: DATABASE_URL. Объявите аргумент с заглушкой, настоящее значение отдайте в рантайме:
ARG DATABASE_URL="postgresql://build:build@localhost:5432/build"
ENV DATABASE_URL=$DATABASE_URL
RUN npx prisma generate && npm run build
Значение вшито в бандл. Всё, что начинается с NEXT_PUBLIC_ или VITE_, попадает во фронтенд на этапе сборки. Поменяли переменную, нажали Deploy — а в браузере старый адрес API: слой со сборкой взялся из кеша. Нужен Redeploy с отключённым кешем либо docker builder prune -af перед выкатом.
Кавычки, CRLF и доллар. Редактор переменных сохраняет строку как есть, поэтому DATABASE_URL="postgres://user:pass@db:5432/app" даёт значение, начинающееся с символа ", а драйвер отвечает невнятным invalid URI query parameter. Проверка — docker exec $(docker ps -q -f name=myapp) printenv DATABASE_URL | od -c | tail -2: ищите " в начале и \r в конце (копирование из Windows). А при деплое через Compose Docker раскрывает $: пароль p$w0rd станет p плюс пустая подстановка, экранируйте как p$w0rd.
Общие переменные проекта. Ссылка ${{project.DATABASE_URL}} на несуществующую переменную не роняет деплой: она разворачивается в пустоту, и приложение падает уже в рантайме с сообщением, по которому причину не угадать. После правки общих переменных перевыкатывайте все приложения проекта.
Какой сервер под Dokploy взять в MAATRIX
Главный вывод разбора: сама панель почти ничего не потребляет, деплои падают из-за сборок. Считайте ресурсы по пику сборки, а не по потреблению работающего приложения.
Минимум: 2 vCPU, 4 ГБ RAM, 60 ГБ NVMe. Панель со службами занимает около 330 МБ, ещё 200–400 МБ — работающее приложение, остальное уходит сборке. Next.js и Nuxt собираются впритык, swap на 4 ГБ обязателен. Честное ограничение: во время сборки боевое приложение на этом же сервере заметно тормозит. Для одного-двух проектов с редкими выкатами нормально, для десятка деплоев в день — нет.
Комфортный вариант: 4 vCPU, 8 ГБ RAM, 100–160 ГБ NVMe. Сборка перестаёт задевать swap и укладывается в 2–4 минуты, рядом спокойно живут Postgres, Redis и три-четыре приложения, места хватает на кеш слоёв и историю образов для отката. Приложений больше десяти — берите 16 ГБ либо выносите сборку на отдельный узел Swarm. Диск не экономьте: 2–4 ГБ на образ плюс 10–15 ГБ кеша; именно место, а не память, чаще всего роняет деплой на второй месяц жизни сервера.
Локация — Великобритания, Лондон. RTT до панели чувствуется каждым кликом: из Москвы до Лондона 45–55 мс против 120–140 мс до Восточного побережья США. Плюс короткий маршрут до GitHub, npm и Docker Hub: сборка тянет сотни мегабайт зависимостей, и канал здесь заметнее, чем на отдаче страниц. Если аудитория российская и есть персональные данные — локация RU и 152-ФЗ; если приложение ходит в зарубежные ИИ-API — US.
Dokploy есть в каталоге apps.maatrix.io и при заказе сервера разворачивается автоматически — вставлять установочный скрипт не нужно. Автоустановка работает на Ubuntu и Debian; адрес панели, логин и пароль появляются в личном кабинете в разделе «Доступ», дальше вы сразу добавляете репозиторий и домен. Оплата — картой российского банка, по СБП, криптовалютой или токеном MAAT. Если ещё выбираете панель, посмотрите Coolify против Dokploy; нужен разбор с нуля — установка Dokploy на VPS.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть DokployОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Деплой падает с exit code: 137, хотя локально всё собирается.
Локально у вас 16 ГБ, на VPS — 2. Код 137 значит, что сборку убил OOM killer: подтвердите через dmesg -T | grep -i 'killed process', добавьте swap на 4 ГБ и переходите на 4–8 ГБ RAM.
Сервис висит в 0/1, в логах ничего нет. Что смотреть?
docker service ps myapp --no-trunc. Строка no suitable node (insufficient resources on 1 node) означает завышенное поле Memory Reservation на вкладке Advanced. Снизьте резервацию до 256–512 МБ.
Деплой успешен, но домен отдаёт 502. Куда копать?
В девяти случаях из десяти — порт: в карточке домена указан не тот Container Port либо приложение слушает 127.0.0.1 вместо 0.0.0.0. Проверьте изнутри контейнера через ss -ltnp.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.