Манифесты: варианты и откуда брать значения
Справочник по полям манифеста и готовые примеры под разные проекты. Общая последовательность действий — в статье «Как добавить приложение через панель».
1. Как устроен манифест
Манифест — это JSON, который вы вставляете в панели в поле манифеста и сохраняете. Панель передаёт его агенту на вашей машине, агент сохраняет файл и дальше работает по нему: клонирует репозиторий, собирает образ, запускает контейнеры.
Обязательное поле одно — app (имя приложения). Остальные имеют значения по умолчанию, поэтому
минимальный манифест состоит из адреса репозитория, ветки и файла-инструкции.
2. Поля манифеста
| Поле | По умолчанию | Зачем |
|---|---|---|
app |
— (обязательно) | имя приложения: строчные латинские буквы, цифры, -, _, до 32 символов; по нему помечаются контейнеры и оно показывается в панели |
repo_url |
— | адрес репозитория по HTTPS (или ssh://, git@…); нужен для клонирования |
branch |
main |
ветка, из которой разворачивается код; у многих проектов основная ветка master — уточните на странице репозитория |
subdir |
. |
папка внутри репозитория, где лежит проект (для монорепозиториев), например services/api |
compose_file |
docker-compose.yml |
имя compose-файла внутри subdir; если файл есть — приложение считается compose-проектом |
services |
[] |
список сервисов compose для запуска; пусто — запускаются все. Например ["api","worker"] |
dockerfile |
Dockerfile |
имя Dockerfile — используется, когда compose-файла нет |
tag |
apppilot/<app>:latest |
имя и метка образа, который собирается из Dockerfile |
image |
— | готовый образ из реестра (например nginx:1.27-alpine): вместо сборки будет docker pull; для проекта с собственным Dockerfile не указывается |
run_args |
[] |
аргументы запуска для проекта без compose: публикация портов (-p), тома (-v), параметры (--memory=512m) |
env |
{} |
переменные окружения приложения, например {"APP_ENV":"production"}; передаются и в compose-запуск, и в отдельный контейнер |
health_url |
— | адрес для проверки кнопкой «Health-check», например http://127.0.0.1:8080/ (запрос идёт с самой машины) |
health_timeout |
5.0 |
сколько секунд ждать ответа по health_url |
Логина и токена в манифесте нет. Доступ к приватному репозиторию задаётся отдельно — в карточке токена на странице «Контейнеры». Токен хранится на вашей машине и не попадает ни в манифест, ни в базу панели, ни в журналы.
3. Пример 1: публичный репозиторий с docker-compose
Самый частый случай: код в корне репозитория, инструкция — docker-compose.yml.
{
"app": "webapp",
"repo_url": "https://github.com/acme/webapp.git",
"branch": "main",
"subdir": ".",
"compose_file": "docker-compose.yml",
"health_url": "http://127.0.0.1:8080/"
}
Порты наружу публикует сам compose-файл (секция ports), панель к этому не причастна. Если health_url
не указать, «Health-check» покажет только состояние и health-статусы контейнеров.
4. Пример 2: приватный репозиторий на GitHub
Манифест отличается от примера 1 только адресом репозитория — токена в нём нет. Токен задаётся отдельно: «Контейнеры» → выберите машину и приложение → карточка токена → вставьте токен → «Сохранить токен». Панель покажет «токен: задан», а сам токен обратно не покажет никогда.
{
"app": "webapp",
"repo_url": "https://github.com/acme/webapp.git",
"branch": "main",
"compose_file": "docker-compose.yml"
}
Где взять адрес: на странице репозитория — кнопка Code → вкладка HTTPS → скопировать адрес
(он оканчивается на .git).
Где взять токен (нужен только на чтение):
- GitHub → аватар → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token: Repository access — «Only select repositories» и ваш репозиторий; Permissions → Repository permissions → Contents: Read-only. Такой токен не даёт доступа ни к чему, кроме чтения кода.
- Либо классический токен (Tokens (classic)) с единственной областью
repo.
Логин при работе по токену GitHub не проверяет: поле «Логин» можно оставить пустым — агент подставит
oauth2. Важен сам токен.
Токен — это пароль. Храните его в надёжном месте и выдавайте минимальные права: чтение кода. Если токен попал к посторонним — отзовите его на хостинге и сохраните в панели новый.
5. Пример 3: приватный репозиторий на GitLab
{
"app": "shop-api",
"repo_url": "https://gitlab.com/acme/shop-api.git",
"branch": "main",
"subdir": ".",
"compose_file": "docker-compose.yml"
}
Токен (любой из вариантов, все — только на чтение репозитория):
- Project Access Token (рекомендуется): проект → Settings → Access tokens → Add new token,
роль Reporter, область
read_repository. - Deploy Token: проект → Settings → Repository → Deploy tokens → включить
read_repository. GitLab сам выдаёт логин и токен — логин берите из карточки токена, а не произвольный. - Личный токен: профиль → Edit profile → Access tokens → область
read_repository.
Логин для project access token и личного токена — любое непустое значение (GitLab его не проверяет):
можно указать oauth2 или имя из карточки токена. Для deploy token логин берётся из карточки токена
и вводится в поле «Логин» рядом с токеном.
Для GitLab берите адрес из кнопки Code → Clone with HTTPS и убедитесь, что он ведёт на тот же домен,
на котором создан токен (например gitlab.com): если хостинг перенаправляет на другой домен,
авторизация по токену теряется.
6. Пример 4: монорепозиторий со своим compose-файлом
Проект лежит в подпапке, а compose-файл называется иначе:
{
"app": "shop-front",
"repo_url": "https://gitlab.com/acme/monorepo.git",
"branch": "release",
"subdir": "apps/frontend",
"compose_file": "compose.prod.yml",
"services": ["web", "proxy"]
}
Из одного репозитория можно развернуть несколько приложений — сохраните два манифеста с разными app
и разными subdir. Требование одно: имена приложений должны быть разными, а сервисы — не занимать
один и тот же порт на машине.
7. Пример 5: проект без compose, только Dockerfile
Если compose-файла нет, агент собирает образ из Dockerfile и запускает одиночный контейнер.
Порт публикуется через run_args:
{
"app": "tictactoe",
"repo_url": "https://gitlab.com/my/tictactoe-app.git",
"branch": "main",
"dockerfile": "Dockerfile",
"tag": "tictactoe:latest",
"run_args": ["-p", "0.0.0.0:8080:80"],
"health_url": "http://127.0.0.1:8080/"
}
8. Пример 6: готовый образ из реестра
Свой код не нужен — например, база данных или кэш для вашего приложения:
{
"app": "cache",
"image": "redis:7-alpine",
"run_args": ["-p", "127.0.0.1:6379:6379", "--appendonly", "yes"]
}
Учтите: образ из реестра не содержит вашего кода, поэтому «Клонировать» и «Собрать» для него не нужны —
достаточно «Запустить всё». Адрес 127.0.0.1 в run_args оставляет порт доступным только внутри
машины (для базы или кэша это обычно то, что нужно).
9. Где взять каждое значение
| Значение | GitHub | GitLab |
|---|---|---|
Адрес репозитория (repo_url) |
страница репозитория → Code → HTTPS | страница проекта → Code → Clone with HTTPS |
Основная ветка (branch) |
на главной странице в переключателе веток; сменить — Settings → Branches → Default branch | проект → Settings → Repository → Default branch |
| Токен для чтения кода | Settings → Developer settings → Personal access tokens: fine-grained (Contents: Read-only) или classic с областью repo |
проект → Settings → Access tokens (read_repository) или Settings → Repository → Deploy tokens |
| Логин (поле «Логин») | имя аккаунта или x-access-token — при токене значение не проверяется |
для project/личного токена — любое непустое (oauth2); для deploy token — логин из карточки токена |
| Токен (поле «Токен») | сам токен (после закрытия страницы он не показывается — храните копию) | сам токен |
Файл-инструкция (compose_file, dockerfile) |
имя файла в репозитории; если он в подпапке — путь к папке в subdir, а в поле файла остаётся только имя |
то же |
| Порт приложения | в проекте с compose — секция ports; в проекте с Dockerfile — строка EXPOSE подсказывает порт, а публикует его run_args |
то же |
10. Частые ошибки в манифестах
| Что происходит | Причина | Как исправить |
|---|---|---|
| панель не принимает манифест: «app: допустимы…» | в имени приложения заглавные буквы, пробелы, точки, кириллица или длина больше 32 | оставьте строчные латинские буквы, цифры, - и _, например shop-api |
| клонирование просит логин или отвечает «Authentication failed» | приватный репозиторий без токена, заполнено только одно из полей логина/пароля, токен просрочен или хостинг перенаправляет на другой домен | заполните оба поля, выпустите новый токен, возьмите адрес из кнопки «Clone» (при перенаправлении — конечный домен) |
| «ветка не найдена» | в репозитории основная ветка master, а в манифесте main (или наоборот) |
посмотрите имя основной ветки на странице репозитория и поправьте branch |
| «compose-файл не найден» | файл называется docker-compose.yaml, лежит в подпапке или не закоммичен |
проверьте compose_file и subdir; убедитесь, что файл есть в разворачиваемой ветке |
| сборка «пропускает» изменения в коде | Docker переиспользует кэш слоёв | нажмите «Пересобрать» (сборка без кэша), перед этим — «Клонировать репозиторий» |
порт занят (address already in use) |
такой порт на машине уже слушает другое приложение | назначьте другой порт в ports/run_args: у разных приложений порты на машине не должны совпадать |
| приложение не отвечает на «Health-check» | health_url указывает не на тот порт или на внешний адрес |
проверку задавайте на адрес внутри машины: http://127.0.0.1:порт/ |
| «Обновить образ» не даёт эффекта | образ собирается на машине, в реестре его нет | используйте «Пересобрать» |
11. Что платформа не разрешает
Проверка идёт перед сборкой и запуском — и в панели, и на самой машине. Запрещено то, что даёт контейнеру права машины хоста:
| Запрещено | Почему |
|---|---|
privileged: true, --privileged |
контейнер получает доступ к устройствам и ядру хоста |
network_mode: host, --network host, network_mode: container:… |
контейнер живёт в сети машины: видит внутренние порты и сервисы |
pid/ipc/uts/userns: host |
общие пространства имён с хостом |
cap_add с SYS_ADMIN, NET_ADMIN, SYS_PTRACE, ALL и подобными |
capability, которые обходят изоляцию |
devices, --device, --volumes-from, --sysctl |
проброс устройств и параметров ядра |
security_opt: seccomp=unconfined, apparmor=unconfined, label=disable |
снимает защиту контейнера |
тома с путями хоста вне каталога приложения, включая /var/run/docker.sock |
сокет Docker — это root на машине; чужие каталоги хоста — чужие данные |
build.context и env_file с абсолютным путём или .. |
сборка и чтение файлов вне каталога приложения |
Разрешено всё обычное: публикация портов, именованные тома и тома внутри каталога приложения,
healthcheck, restart, лимиты, переменные окружения, cap_drop, read_only.
Если отказали, панель покажет конкретное нарушение — уберите эту настройку и повторите. Нужен полный контроль над контейнерами и доступ к системе машины — заказывайте виртуальную машину: там root выдаётся, и ограничений платформы нет.
12. Полезно знать
- Переменные из
envвидны в контейнере как обычные переменные окружения. Секреты в манифесте допустимы, но помните: он хранится на вашей машине в открытом виде — для токенов и паролей лучше использовать отдельные механизмы (токен репозитория задаётся вне манифеста). - Один аккаунт репозитория можно использовать для нескольких приложений, но лучше завести отдельный read-only токен на каждое приложение: так проще отзывать доступ.
- Удаление приложения в панели убирает контейнеры; чтобы вернуть приложение, достаточно сохранить манифест и повторить клонирование, сборку и запуск.
- Если репозиторий закрыт совсем (без токенов) или лежит в вашей внутренней сети, доступный вариант — собрать и запустить приложение по SSH самостоятельно; панель покажет контейнеры, но операции уровня приложения к ним не применит.