Перейти к основному содержимому

layero.json — конфигурация в репозитории

Необязательный файл в корне приложения. Он задаёт то, что Layero обычно определяет сам, и лежит рядом с кодом — значит, едет вместе с ним и может отличаться по веткам.

Правило одно:

Всё, что вы указали в файле, Layero обязан применить. Всё, чего вы не указали, Layero определяет сам.

Сначала деплой без файла​

Первый деплой делайте без layero.json. По данным платформы за 90 дней, 84 % новых проектов публикуются с первой сборки без файла: Layero сам находит фреймворк, пакетный менеджер, скрипт сборки, папку результата, версию Node, а у серверного приложения — команду запуска и порт.

Файл — это ответ на конкретный отказ: один симптом — одно поле.

  • Каждое записанное поле перестаёт определяться автоматически и закрывается в панели. Значение, скопированное «на всякий случай», станет неправдой в тот день, когда проект сменит сборщик.
  • Около 60 % упавших сборок файл не лечит — они чинятся в другом месте, см. чего файл не лечит.

Порядок действий, когда сборка или запуск упали:

  1. Прочитайте настоящую ошибку, а не карточку деплоя: в логе сборки (npx layero@latest logs, для вывода работающего приложения — logs --runtime) или через npx layero@latest diagnose.
  2. Найдите симптом в таблице ниже и поменяйте ровно одно.
  3. Проверьте по логу, что значение применилось и файл не дал замечаний.
  4. Та же ошибка второй раз подряд — остановитесь и перечитайте лог, а не деплойте в третий. После десяти одинаковых отказов подряд Layero приостанавливает автодеплой из git.

Файл оправдан и без отказа, когда конфигурация должна ехать вместе с кодом: отличаться по веткам или проходить код-ревью.

Примеры: один симптом — одно поле​

Не та папка результата — и больше ничего:

layero.json
{ "outputDirectory": "dist/client" }

Проект определился как «Static (no build)», а это приложение на Vite:

layero.json
{ "framework": "vite", "outputDirectory": "dist" }

Свой скрипт сборки без известного фреймворка:

layero.json
{ "framework": "generic", "buildCommand": "node build.mjs", "outputDirectory": "public" }

Node-сервер, который Layero принял за статический сайт:

layero.json
{ "runtime": "node_web", "startCommand": "node dist/server.js", "port": 3000 }

FastAPI с нестандартной точкой входа:

layero.json
{ "runtime": "python_web", "startCommand": "uvicorn app.main:app --host 0.0.0.0 --port $PORT" }

Все поля необязательны. Пустой файл {} допустим и означает «определяй всё сам».

"$schema": "https://layero.ru/schema/layero-v2.json" подключает автодополнение и подсветку ошибок в редакторах с поддержкой JSON Schema: VS Code, JetBrains, Neovim. На сборку это поле не влияет.

Все ключи​

Существуют только эти имена. Неизвестный ключ сборку не роняет — он пропускается с замечанием в логе, см. ошибки в самом файле.

Ключ (короткое имя)К чему относитсяЧто задаёт
frameworkсборкаИмя фреймворка. Перекрывает автоопределение. Значения — в списке фреймворков.
installCommand (install)сборкаКоманда установки зависимостей.
buildCommand (build)сборкаКоманда сборки целиком.
outputDirectory (output)только статикаПапка с index.html после сборки.
nodeVersion (node)сборкаВерсия Node.js.
runtimeтип приложенияЗапускать приложение в контейнере, а не раздавать файлами.
startCommand (start)только контейнерКоманда запуска внутри контейнера.
portтолько контейнерПорт, который слушает приложение.
memory_mb, cpu_quota, idle_timeout_s, preloadтолько контейнерРесурсы контейнера и время простоя до засыпания.
envтолько контейнерНесекретные переменные окружения.
layoutраскладка"fullstack" — явно объявить две половины.
frontendфуллстекПоловина, которая раздаётся файлами.
backendфуллстекПоловина, которая работает в контейнере.
apiPrefix (api_prefix)фуллстекПрефикс пути, который уходит на бэкенд, например "/api".

framework​

Имя фреймворка. Задавайте, только когда автоопределение ошиблось: Storybook рядом с приложением на Vite, «Static (no build)» у проекта, которому нужна сборка.

Два значения стоит различать:

  • static — сборка не запускается вовсе. Ни установки, ни сборки: buildCommand с ним игнорируется, публикуется то, что лежит в папке.
  • generic — свой скрипт сборки. Известного фреймворка нет, но собрать проект нужно: Layero исполнит ваши buildCommand и outputDirectory.

installCommand​

Команда установки зависимостей. По умолчанию Layero берёт воспроизводимую команду вашего пакетного менеджера: npm ci, yarn install --frozen-lockfile, pnpm install --frozen-lockfile.

Сначала чините репозиторий: закоммитьте актуальный lock-файл и точный packageManager — и только потом задавайте поле. npm ci без lock-файла — готовый отказ.

У приложения в контейнере поле действует только для Python: заменяет шаг pip install -r requirements.txt. Node-сервер (node_web, ssr_next) его не исполняет — установка выбирается по lock-файлу.

buildCommand​

Команда сборки целиком: pnpm build:prod, а не build:prod. Не пишите в файл npm run build — это и так умолчание, а запись закроет поле в панели.

outputDirectory​

Папка с готовой статикой относительно корня приложения — та, где после сборки лежит index.html. Например: dist, build, out, dist/client, dist/my-app/browser. Указывается папка, а не файл. Layero исполняет написанное и не подменяет заданную папку более удачной догадкой.

Поле действует только для сайтов, которые Layero раздаёт файлами. У приложения в контейнере оно принимается, но не применяется — и замечания об этом нет. Такому приложению задайте startCommand.

nodeVersion​

Версия Node.js, на которой собирать. Принимает мажор "22", точную версию "22.14.0" или "lts". Поле перекрывает .nvmrc и engines.node: если версия уже задана там, правьте её там же, иначе у проекта появятся два источника правды.

runtime​

Тип приложения, которое Layero запускает в контейнере: ssr_next, node_web, python_web, streamlit, gradio (двум последним нужен app.py). Других значений нет.

Обычно Layero сам переключает проект между статикой и контейнером, когда узнаёт сервер: Next без output: 'export', Express, Fastify, FastAPI, Flask, Django. Поле нужно, когда Layero сервер не узнал. runtime в корне файла сильнее типа проекта в настройках — в логе будет the file wins. Одна строка меняет способ раздачи сайта.

Разовая замена без файла: npx layero@latest deploy -t node_web. Подробнее о контейнерах — Когда нужен runtime.

startCommand​

Команда запуска приложения в контейнере. Без runtime (или серверного типа проекта) ничего не делает.

Рабочая папка приложения в контейнере — /app: туда попадает ваш код, там же Layero устанавливает зависимости и собирает проект. Пути в команде отсчитываются от неё: node server.js запустит /app/server.js. Файл из команды должен существовать после сборки.

Если поле не задано, Layero определяет команду сам: у Node-приложения это обычно npm start из package.json. Скрипта start нет — задайте startCommand.

Приложение обязано слушать 0.0.0.0 и порт из переменной $PORT, например uvicorn main:app --host 0.0.0.0 --port $PORT. Сервер на 127.0.0.1 снаружи контейнера недоступен. ASGI-приложению нужен uvicorn, а не gunicorn app:app.

port​

Порт, который слушает приложение, если он отличается от умолчания: Node — 3000, Next — 8080, Python — 8000, Streamlit — 8501, Gradio — 7860.

memory_mb, cpu_quota, idle_timeout_s, preload​

Ресурсы контейнера и время простоя, после которого приложение засыпает. memory_mb — память работающего контейнера, а не сборки: нехватку памяти на сборке поле не лечит.

env​

Несекретные переменные для приложения в контейнере — и на сборке, и при запуске. Имя, заданное здесь, перекрывает одноимённую переменную проекта. Статическая сборка это поле не читает.

Файл лежит в git — секретам в нём не место. Секреты задаются переменными окружения проекта.

Фуллстек: фронт и бэк в одном репозитории​

Фронтенд раздаётся файлами, бэкенд работает в контейнере, API живёт под /api:

layero.json
{
"frontend": { "root": "frontend", "output": "dist" },
"backend": { "root": "backend", "framework": "fastapi" },
"apiPrefix": "/api"
}
  • Внутри половин — короткие имена. У frontend: root, framework, install, build, output, node. У backend: root, framework, runtime, install, start, node, port, memory_mb, cpu_quota, idle_timeout_s, preload, env. Длинные имена из корня файла (buildCommand, outputDirectory, startCommand) внутри блоков не действуют.
  • Нужны оба блока. Один блок раскладку не включает — с "layout": "fullstack" или без него.
  • Бэкенду обычно хватает root и framework (fastapi, express, django…): для FastAPI, Flask, Django и Express команда запуска и порт выводятся сами. start и port добавляйте, только если запуск упал.
  • root — существующая папка. У половины, которая лежит в корне репозитория, root пустой или не задан. Если папки нет, сборка упадёт, а ошибка перечислит настоящие папки — берите путь оттуда.
  • backend.runtime тип контейнера не выбирает: решает настройка проекта, а о расхождении сказано только в логе.
  • Блоки frontend и backend целиком заменяют фуллстек-настройки из панели. Блоки с выдуманными ключами (dir, build_cmd, start_cmd) выключат эти настройки и ничего не дадут взамен: проект, который собирался, перестанет собираться. Если проект уже собирается по настройкам панели, блоки не добавляйте.

Короткие имена работают всегда​

У шести полей есть короткая форма, и она не устаревшая: файлы, написанные короткими именами, будут работать всегда.

Основное имяКороткое
installCommandinstall
buildCommandbuild
outputDirectoryoutput
nodeVersionnode
startCommandstart
apiPrefixapi_prefix

Если в файле окажутся оба имени одного поля, применится основное. Про второе Layero скажет в логе сборки: иначе вы правили бы не ту строку.

Симптом → что править​

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

СтадияГде чинить
detectФорма приложения: корневая папка, тип проекта, runtime. Не код.
installLock-файл и packageManager в репозитории, затем installCommand.
buildСтрока ошибки из лога: обычно код, иногда buildCommand или nodeVersion.
verify, uploadoutputDirectory.
launchstartCommand, port, переменные окружения.
clone, timeout, activateПлатформа или доступ к источнику. Один повтор.

Что лечит файл​

СимптомЧто править
собранный сайт не содержит index.html в '.' · сборка отработала, но папки 'dist' нет. После сборки в каталоге есть: … · [output] в 'dist' нет index.htmloutputDirectory — папка, где после сборки лежит index.html. Ошибка перечисляет, что есть на диске, — путь берите оттуда.
Сборка НЕ запускалась: проект собирается как статический сайт (фреймворк «static») … · в логе [config] ВНИМАНИЕ: buildCommand из layero.json (…) НЕ выполняется или static framework: skipping install/buildСборка не запускалась: framework: "static" значит «без установки и без сборки». Поставьте "framework": "generic". Путь к папке тут ни при чём.
npm error Missing script: "build" · не знаем, чем собирать этот проект: команда сборки не заданаbuildCommand с существующим скриптом. Сборка не нужна вовсе — "framework": "static". Если это сервер или бот — следующая строка.
Сервер на Node или Python опубликован как статика: файлы отдаются, ничего не работаетruntime + startCommand, при необходимости port. Без файла: deploy -t node_web.
launch/boot: container failed to start in time, в логе ERR_MODULE_NOT_FOUND /app/… или приложение слушает 127.0.0.1startCommand: путь от /app, файл существует после сборки, адрес 0.0.0.0, порт $PORT. Другой порт — port.
ERR_UNKNOWN_BUILTIN_MODULE · EBADENGINE · Node.js 18 снят с поддержки и закрыт для новых сборок"nodeVersion": "22" — но сначала посмотрите строку [config] node=…: если версию задаёт .nvmrc или engines.node, правьте там.
npm error code EUSAGE · ERR_PNPM_…Сначала lock-файл и packageManager в репозитории, потом installCommand.
/bin/sh: 1: run: not foundКоманда записана обрывком. Нужна целая: npm run build, а не run build.
Can't resolve '@scope/shared' в воркспейсеДеплойте из корня воркспейса, а не с --root apps/web: иначе соседний пакет не попадёт в сборку. В файле нужны три поля: "framework": "generic", buildCommand, который сначала собирает зависимости (pnpm --filter @scope/shared build && pnpm --filter @scope/web build), и "outputDirectory": "apps/web/dist". Задайте framework явно: CLI до 0.11.0 записывал корень воркспейса в проект как static, и сборка не запускалась вовсе.
Фронт и бэк в одном репозитории, а опубликована только статикаБлоки frontend + backend.

Чего файл не лечит​

СимптомЧто на самом деле чинит
Монорепо: no app at the repo root, and multiple candidate app folders found (…) · root_directory not found in source · no package.json at the build rootКорень приложения — настройка проекта, а не ключ файла. npx layero@latest deploy --root apps/web (сохраняется на проекте) или поле «Папка приложения» в панели. Ключа rootDirectory не существует.
branch 'cli' has no app to build: no package.json / layero.json / requirements.txt found at the deploy rootdeploy запущен не из той папки. Перейдите в папку приложения или передайте --root. Готовая статика — --prebuilt <dir>.
Next.js: "/app/.next/standalone": not found · project is configured for SSR, can't deploy as staticРежим Next задаётся в next.config, а не в файле: output: 'export' — статика, без output — сервер. outputDirectory: ".next" не пишите никогда.
error TS… · Type error: · Module not found: Can't resolve '@/…' · Failed to compileКод. Воспроизведите локально той же командой и тем же мажором Node. Частая причина — регистр в имени файла: macOS его прощает, Linux нет.
launch/probe: container bound but not servable (5xx)Окружение, база, код: GET / должен отвечать без 5xx. Переменные задаются в панели или npx layero@latest env set, не в файле.
supabaseUrl is required · Failed to collect page dataПеременные окружения проекта, нужные на сборке. Секреты в файл не кладут.
builder did not pick up the job after N retries · auth.docker.io … TLS handshakeСбой платформы. Один повтор; повторилось — пишите в поддержку, код и файл не трогайте.
docker-build: timeout after …s · Reached heap limit · ENOSPCЛимиты сборки на тарифе. memory_mb — память контейнера, а не сборки.
хост '…' не в списке разрешённых источниковПодключение источника в панели.

Что сильнее чего​

Для каждого поля отдельно:

layero.json → настройки проекта → подсказка CLI (--type) → найденное в репозитории → умолчание фреймворка

Побеждает первое заданное. Например, при таком package.json:

{ "scripts": { "build": "vite build" } }

и таком layero.json:

{ "buildCommand": "npm run build:production" }

соберётся npm run build:production.

У nodeVersion версию умеет объявлять и сам репозиторий:

layero.json → настройки проекта → .nvmrc → .node-version → engines.node → умолчание Layero

Перекрытую версию из репозитория Layero называет в логе сборки, а не применяет молча.

Файл или настройки проекта? Файл — когда значение должно ехать с кодом: в каждую ветку и каждый клон. Настройки проекта (--type, --root, панель) — для разовой правки и для всего, у чего нет ключа в файле.

Как проверить по логу​

До выкатки npx layero@latest deploy --dry-run покажет план: фреймворк, команду сборки, папку результата и источник каждого значения. Значения из этого файла там помечены layero.json. Окончательное доказательство — лог настоящей сборки. После каждой правки найдите в нём две вещи.

1. Применённое значение с пометкой источника. Статическая сборка:

[config] framework=vite (from layero.json)
[config] build=`npm run build:production` (from layero.json)
[config] install=`pnpm install` (from layero.json)
[config] output=dist/client (from layero.json)
[config] node=22.18.0 (layero.json)

У строки про Node слова from нет. Любой другой источник значит, что ваше поле не применилось: (from dashboard), (from hint), (auto-detected), (from package.json scripts), (from lockfile), (default for vite), (project settings), (.nvmrc), (engines.node), (default).

Сборка контейнера:

runtime build started (kind=node_web)
start command: node dist/server.js
env: 3 from project, 1 from layero.json

start command печатается без источника — сравните текст со своим startCommand. У Python есть ещё строка install command: … (источник: layero.json).

2. Замечаний от файла нет. Любая из этих строк значит, что файл сделал не то, что вы думаете:

[config] layero.json: unknown keys ignored: …
[config] layero.json: invalid JSON at line N col N
[config] layero.json: `build` must be a non-empty string — ignored
[config] layero.json: `buildCommand` и `build` — одно и то же поле; применяется `buildCommand`
[config] WARNING: unknown framework '…' (layero.json). Known: …. Auto-detected as '…'.
layero.json requests runtime '…' while the project is set to '…' — the file wins
[config] в layero.json объявлен бэкенд «…», но проект настроен как «…» — собираем по настройке проекта
[config] layero.json: объявлен `layout: "fullstack"`, но `backend` не описан — раскладка не применится
[output] в '…' нет index.html. Платформа не подменяет заданный каталог
[config] предупреждение: `…` слушает только localhost — контейнер будет недоступен
Фронтенд указан в каталоге …, но такого каталога в репозитории нет

Последняя строка (или такая же про бэкенд) сборку роняет. Строка [config] node: layero.json перекрыл .nvmrc=… — справочная: у версии теперь два источника.

Потом проверьте не сборку, а результат: откройте адрес из события ready. Зелёная сборка с упавшим запуском — всё равно отказ. У приложения в контейнере первые запросы после ready могут до минуты получать заглушку 404, пока контейнер стартует. Если 404 держится дольше минуты, это настоящий отказ: читайте logs --runtime.

Поле из файла нельзя изменить в панели​

Если поле объявлено в layero.json, панель показывает его значение и бейдж с именем файла — вместо кнопки редактирования. Нажмите на бейдж — откроется файл в репозитории.

Так сделано намеренно. Открытое поле — это обещание, что правка применится, а правку в панели первая же сборка отменила бы: файл сильнее. Меняйте такое значение в репозитории — там, где оно задано.

Такое поле не участвует и в сохранении: панель не запишет в настройки проекта значение, которое всё равно проиграет файлу.

Где должен лежать файл​

  • Обычный проект — в корне репозитория.
  • Монорепо — в папке приложения, а не в корне репозитория. Указали apps/web (флагом --root или в настройках проекта) — Layero читает apps/web/layero.json. Исключение — фуллстек: половины задают свои папки через frontend.root и backend.root.

Ошибки в самом файле​

Layero почти никогда не отказывается собирать проект из-за layero.json: сборку роняет только несуществующая папка в frontend.root или backend.root. Обратная сторона: ошибка в файле не останавливает сборку, и легко поверить, что настройка сработала. Замечания попадают в лог сборки, в мастер настройки и на страницу деплоя — но не все и не всегда.

Что в файлеЧто делает Layero
невалидный JSON (лишняя запятая, комментарий)файл выключается целиком, сборка идёт так, как будто его нет. Статическая сборка пишет замечание, сборка контейнера молчит
неизвестное имя поляпропускает с замечанием unknown keys ignored — сборка при этом зелёная. Так незаметно пропадали static, headers, type, dir, build_cmd, output_dir, start_cmd, install_cmd, rootDirectory. Ключ type пишут по аналогии с флагом --type: он не применяется, и замечание называет верное имя — framework или runtime (deploy --dry-run предупреждает об этом до выкатки)
пустое значениесообщает, поле пропускает
неизвестное имя в framework (например, "node")предупреждает и определяет фреймворк сам
framework, которого нет в репозиториипредупреждает и всё равно применяет

Проверяйте JSON до коммита. И ещё пять ошибок, о которых Layero чаще всего не скажет ничего:

  1. «Анкета» вместо правки. Все поля заполнены умолчаниями: npm ci, npm run build, dist. Это самый частый файл и самый бесполезный: он ничего не меняет, но закрывает поля в панели.
  2. Закреплённое неверное значение. "output": "." у проекта со сборкой, "output": ".next", "install": "true" — исполняется буквально, ничего не ставится. Layero делает то, что написано.
  3. outputDirectory у приложения в контейнере и startCommand или port без runtime: принимаются, хранятся, не используются — замечания нет.
  4. Блоки frontend и backend отменяют фуллстек-настройки панели — даже если внутри выдуманные ключи. См. Фуллстек.
  5. Файл не в той папке монорепо и секреты в env.

Адрес схемы​

https://layero.ru/schema/layero-v2.json

Прежний адрес продолжает работать и не будет удалён:

https://layero.ru/schema/layero-v1.json

Если в ваших репозиториях стоит v1, менять его не нужно — файл читается одинаково. В новых проектах указывайте v2: в нём описаны основные имена полей, и редактор не подчёркивает как ошибку то, что Layero принимает.