# Настройки сборки и свой Dockerfile

> Команды сборки и запуска, корневая папка монорепозитория, версия рантайма, переменные сборки и свой Dockerfile, если автоопределения мало.

Раздел: Деплой
Источник: https://nodbox.ru/docs/deploy/build-settings/

---
Платформа сама выбирает, как собрать приложение из исходников, а семь полей приложения позволяют этот выбор поменять: сборщик, корневая директория, команды сборки и запуска, версии рантайма и два набора переменных сборки. Если в корне сборки лежит Dockerfile, платформа собирает образ по нему, и писать для этого ничего не нужно.

Настройки сборки есть у приложений из репозитория и из архива. У приложения из готового образа сборки нет, и любое из этих полей отвечает ошибкой `422`. Меняются настройки через API: на странице приложения в панели их нет.

## Что настраивается

| Поле | Что делает | По умолчанию |
| --- | --- | --- |
| `builder` | выбирает способ сборки: `auto`, `railpack` или `dockerfile` | `auto` |
| `root_directory` | каталог внутри репозитория или архива, из которого идёт сборка | корень |
| `build_command` | команда сборки вместо той, что выбрала платформа | выбирает платформа |
| `start_command` | команда запуска вместо той, что выбрала платформа | выбирает платформа |
| `runtime_versions` | версии языков и инструментов, которые нужны сборке | выбирает платформа |
| `build_env` | переменные, видные только сборке | пусто |
| `build_secrets` | секреты, видные только сборке | пусто |

Автоопределение языка, зависимостей и команды запуска разбирает страница [Сборка без Dockerfile](/docs/deploy/build/). Здесь только то, чем его можно переопределить.

## Как изменить настройки

Поля задаются при создании приложения в теле запроса `POST /api/v1/applications` и меняются запросом `PATCH /api/v1/applications/{id}`. Правится ровно то, что прислано, остальное остаётся как было. Все запросы и права токена описаны в разделе [API nodbox](/docs/automation/api/).

Пример для приложения из монорепозитория:

```json
{
  "root_directory": "apps/web",
  "build_command": "pnpm --filter web build",
  "start_command": "node dist/server.js",
  "runtime_versions": { "node": "22" },
  "build_env": { "NODE_ENV": "production" },
  "build_secrets": { "NPM_TOKEN": "значение секрета" }
}
```

Текущие значения лежат в блоке `build` ответа `GET /api/v1/applications/{id}`. Ключи в нём те же, что в запросе.

## Настройки действуют со следующей версии

Правка настроек ничего не деплоит. Она ложится на приложение, а каждая новая версия снимает настройки в момент заказа и собирается по этому снимку. Работающая версия остаётся такой, какой была.

Из этого следует два правила:

- Новые настройки заработают, когда закажешь версию или запушишь в ветку с включённым автодеплоем.
- Версия, которая уже собирается, не заметит правку, сделанную после её заказа.

Откат возвращает версию с уже собранным образом, поэтому сборка при откате не повторяется. Подробнее - в разделе [Версии и откат](/docs/deploy/versions/).

## Чем собирать: поле builder

Поле `builder` решает, чем собирать образ.

| Значение | Что происходит |
| --- | --- |
| `auto` | есть `Dockerfile` в корне сборки - образ собирается по нему, нет - платформа определяет язык сама |
| `railpack` | образ собирает платформа, даже если `Dockerfile` лежит рядом: он может быть нужен только для локальной разработки |
| `dockerfile` | образ всегда собирается по `Dockerfile`; если файла нет, версия падает с причиной `dockerfile_missing` |

Явный `dockerfile` не уходит в автоопределение молча: выбранный способ платформа не подменяет другим. Что именно выбрано, видно в начале [лога сборки](/docs/observability/build-log/). Например, при `auto` там будет строка «В корне сборки найден Dockerfile - образ собирается по нему.»

Команды сборки и запуска и версии рантайма действуют только в сборке, которую ведёт платформа. Если образ собирается по Dockerfile, всё это записано в самом файле. Поэтому при `builder: dockerfile` поля `build_command`, `start_command` и `runtime_versions` запрещены: запрос с ними отвечает `422`. Платформа судит тот сборщик, который будет у приложения после запроса. Запрос `{"builder": "railpack", "build_command": "make"}` для приложения с Dockerfile проходит, а `{"builder": "dockerfile", "build_command": "make"}` нет.

Переход на `dockerfile` ранее заданные команды и версии не стирает: они вернутся в дело вместе с автосборкой. Стереть их можно явно: `null` для команд и `{}` для версий. Если при `auto` команды заданы, а Dockerfile найден, в логе сборки появится строка о том, что они не применяются.

## Монорепозиторий: корневая директория

Поле `root_directory` задаёт каталог, из которого идёт сборка. Для приложения из монорепозитория это `apps/web` или другой путь до его папки с `package.json` или `Dockerfile`. Всё, что лежит выше этого каталога, сборке не видно: он становится её контекстом.

Правила записи пути:

- путь считается от корня репозитория или архива, без ведущего слэша
- `.` и `..` в пути запрещены
- завершающий слэш отбрасывается: `apps/web/` и `apps/web` - один и тот же каталог
- пустое значение или `null` возвращают сборку из корня

Каталога может не оказаться в исходниках версии, или путь может вести через символическую ссылку за их пределы. В обоих случаях версия падает с причиной `root_directory_missing`, а лог сборки просит проверить путь в настройках. Если указан `root_directory`, то и `Dockerfile` ищется в нём, а не в корне репозитория.

Для автодеплоя монорепозитория есть отдельный фильтр путей: [Автодеплой по push](/docs/deploy/auto-deploy/). Корневая директория выбирает, что собирать, а фильтр - при каких изменениях запускать сборку.

## Команды и версии рантайма

`build_command` и `start_command` переопределяют выбор платформы. Это одна строка для оболочки, до 512 символов и без управляющих символов вроде перевода строки. Пустая строка и `null` значат «пусть решает платформа».

```json
{
  "build_command": "npm run build:prod",
  "start_command": "node dist/server.js"
}
```

`runtime_versions` - объект вида «имя инструмента: версия». Платформа не знает язык приложения до сборки, поэтому версия указывается вместе с именем инструмента:

```json
{
  "runtime_versions": { "node": "22", "python": "3.12" }
}
```

Инструментов не больше десяти. Имя пишется строчными латинскими буквами, цифрами и дефисом, начинается с буквы. Версия - это строка вроде `22`, `3.12` или `1.22.5`, без пробелов и знака `@`. Набор заменяется целиком: присланный объект и есть новый набор, `{}` возвращает выбор платформе.

## Переменные сборки и переменные приложения

У приложения четыре набора переменных, и они не пересекаются. Переменная сборки не попадает в работающее приложение, а переменная приложения не видна сборке.

| Набор | Кто видит значение | Что отдаёт API |
| --- | --- | --- |
| `env` | работающее приложение | значения |
| `secrets` | работающее приложение | только имена |
| `build_env` | сборка | значения |
| `build_secrets` | сборка | только имена |

Пара `env` и `secrets` описана на странице [Переменные и секреты](/docs/apps/env/). Одно и то же имя, например `NODE_ENV`, можно задать и в `env`, и в `build_env` с разными значениями: это две настройки двух разных этапов.

Правила у `build_env` и `build_secrets` такие же, как у рантаймовых наборов:

- имя состоит из латинских букв, цифр и подчёркивания и не начинается с цифры
- имя не длиннее 128 символов, значение не длиннее 4 096 символов
- переменных и секретов сборки вместе не больше 100
- одно имя не может быть одновременно в `build_env` и в `build_secrets`: запрос отвечает `422` с названием имени
- несколько системных имён, например `PATH` и `HOME`, заняты платформой, и ответ `422` называет такое имя

Правятся наборы по-разному:

- `build_env` заменяется целиком. Присланный объект и есть новый набор, `{}` убирает все переменные, а если поле не прислать, набор не тронут.
- `build_secrets` накладывается поверх прежнего набора. Присланное имя получает новое значение, `null` под именем стирает секрет, неназванные остаются. Пустая строка - это значение, а не удаление.

Значения секретов сборки наружу не возвращаются никогда: по API в блоке `build` лежит список имён. Поэтому прислать набор целиком было бы нечем. Чтобы превратить переменную сборки в секрет, одним запросом пришли `build_env` без неё и `build_secrets` с ней.

В автосборке значения обоих наборов доступны командам сборки. В Dockerfile между ними есть разница, она разобрана ниже.

## Свой Dockerfile

Положи файл с именем `Dockerfile` в корень сборки, и при `builder: auto` образ соберётся по нему. Корень сборки - это корень репозитория или архива, а если задана `root_directory`, то этот каталог.

### Что ожидает платформа

- Файл называется ровно `Dockerfile`, с заглавной буквы. Имена `dockerfile`, `Containerfile` и `Dockerfile.prod` платформа не подхватывает, как и файл не в корне сборки. При `auto` она в этом случае определит язык сама, а при `dockerfile` версия упадёт с `dockerfile_missing`.
- Команду запуска платформа берёт из самого Dockerfile, из `CMD` или `ENTRYPOINT`: в настройках приложения она при этом запрещена.
- Образ может работать от root. Права внутри контейнера те же, что в обычном контейнере Docker, кроме отправки сырых сетевых пакетов.
- Порт веб-приложения платформа берёт из настройки приложения, а не из строки `EXPOSE`. Слушай тот порт, который указан в настройках, он же передаётся в переменной `PORT`. Подробнее - в разделе [Порт, протокол и проверка готовности](/docs/apps/runtime/).
- Сборка идёт до 16 минут, потом платформа её останавливает. Образ, который не помещается в лимит, закрывает версию причиной `image_too_large`.

### Контекст сборки

Контекстом служит корень сборки целиком. Пути в `COPY` и `ADD` считаются от него, и файлы выше него недоступны. У приложения из монорепозитория поэтому задай `root_directory: "apps/web"` и копируй пути относительно `apps/web`, а не от корня репозитория.

### Переменные в Dockerfile

Переменная сборки приходит аргументом, секрет сборки приходит только монтированием в шаг `RUN`:

```dockerfile
FROM node:22-alpine
WORKDIR /app

ARG VITE_API_URL
COPY package*.json ./
RUN npm ci

COPY . .
RUN --mount=type=secret,id=SENTRY_AUTH_TOKEN \
    SENTRY_AUTH_TOKEN="$(cat /run/secrets/SENTRY_AUTH_TOKEN)" npm run build

CMD ["node", "dist/server.js"]
```

Здесь `VITE_API_URL` берётся из `build_env` и читается строкой `ARG`, а `SENTRY_AUTH_TOKEN` из `build_secrets` доступен только этому шагу `RUN`. В слои образа секрет не попадает. Значение `ARG` остаётся в истории образа, поэтому ничего секретного в `build_env` класть нельзя. Переменные приложения из `env` и `secrets` при сборке не видны, а переменные сборки не попадают в работающее приложение: если значение нужно и там, и там, задай его в обоих наборах.

### Что учесть при сборке

- Кэша слоёв между сборками нет: каждая сборка идёт с нуля. Шаг `RUN --mount=type=cache` работает, но кэш в нём при каждой сборке пустой.
- Из сборки открыт интернет: `RUN` может скачивать пакеты и файлы.
- Базовый образ в `FROM` должен быть публичным: логина к закрытым реестрам у сборки нет. Образы с Docker Hub и GitHub Container Registry платформа держит рядом со сборкой, поэтому они скачиваются быстрее.
- Выбрать стадию многостадийной сборки через `--target` и указать другой путь к Dockerfile нельзя: таких настроек у приложения нет.

Если сборка не прошла, причина и строка, на которой она сломалась, видны в [логе сборки](/docs/observability/build-log/). Частые отказы и что с ними делать - на странице [Типичные сбои и что делать](/docs/help/troubleshooting/).

## Что дальше

- [Сборка без Dockerfile](/docs/deploy/build/) - как платформа определяет язык и какие бывают отказы
- [Переменные и секреты](/docs/apps/env/) - переменные работающего приложения
- [Порт, протокол и проверка готовности](/docs/apps/runtime/) - на каком порту слушать
- [Автодеплой по push](/docs/deploy/auto-deploy/) - новая версия на каждый push и фильтр путей
- [Лог сборки](/docs/observability/build-log/) - что платформа пишет о выбранном способе сборки
- [Версии и откат](/docs/deploy/versions/) - как настройки попадают в версию
