Docs · Start
Build settings
Build settings change what detection chose: how the app is built, what the build makes, and how it runs. Every field starts empty, and an empty field means “use what was detected”, shown as the field’s placeholder. Most apps never change one.
Where they are
- In the dashboard: the environment’s Settings tab, under How it builds. When you create an app they are under Build and output settings. Saving changes the next build; what is live keeps running until then.
- For one app, every environment: build settings belong to the app, so every environment of it builds the same way. The health check path is the exception: each environment has its own.
- From the CLI:
homeport deploytakes--run,--releaseand--processfor one deploy, and--savekeeps them for every deploy after it. See the CLI. - In the repo: most of them can also go in an optional homeport.yaml.
Which one wins
A one-off deploy override wins, then the saved build settings, then homeport.yaml, then detection. The homeport.yaml reference has the same order. Three things work a little differently:
- Processes and packages are replaced as a whole. If the build settings name any, homeport.yaml’s are ignored, not merged. While the build settings name none, the dashboard shows homeport.yaml’s under the field.
- Octane and the start command: a start command you set wins over the Octane switch. The Octane switch wins over homeport.yaml’s
run. - Redeploy with changes: on the environment’s page, this changes the start command, release command, processes and Octane. Use once applies them to that deploy alone. Save saves them as the build settings, and a field you empty there goes back to detection.
Every setting
| Dashboard field | homeport.yaml | CLI | Empty means |
|---|---|---|---|
| Root directory | — | — | The repo root |
| What it is | static | — | Detected: app or static site |
| Install | — | — | Detected |
| Build command | build.command | — | Detected |
| Binary / Output folder | build.artifact, static | — | Detected |
| Runtime | runtime | — | Detected, else Node |
| Start command | run | --run | Detected |
| Release command | release | --release | Detected, else none |
| Octane (Laravel) | — | — | On if the app requires laravel/octane |
| Processes | processes | --process | homeport.yaml’s, else none |
| Health check path | — | — | / |
| Packages | packages | — | homeport.yaml’s, else none |
A static site shows only the first five: nothing of it runs, so it has no start command, release command, processes, health check or packages.
How it builds
Root directory
The app’s folder in the repository, for a repo with more than one app in it. Detection and homeport.yaml are read from this folder. The dashboard lists the folders that look like apps. A folder that another app of your team was already made from is shown but can’t be chosen: add an environment to that app instead.
What it is
App is a server that runs, and sleeps when idle. Static site is a folder of files served as they are. Choose the other one when detection guessed wrong. For an rsc-kit app, the build itself says which one it made, unless you choose here.
Choosing a kind that detection didn’t pick turns off homeport’s own packaging for JavaScript and PHP apps. The build is then your build alone, and its output is a file to run (server unless you set Binary) or a folder to serve (dist unless you set Output folder).
Install
Replaces the detected install step, like bun install --frozen-lockfile. It runs before the build command. Anything the build image needs first, such as Bun in a Node image, is still set up before it.
Build command
Replaces your app’s own build, like bun run build. It runs with sh, after the install step. For a JavaScript or PHP app, homeport still packages what your build leaves into the release afterwards. A build command needs a toolchain, so the app’s folder must have a package.json, go.mod or composer.json. For a stack homeport doesn’t detect, use build.image in homeport.yaml.
For a Laravel app, setting a build command turns off Inertia’s server-side rendering bundle and the caching of views and config that homeport does for you, because your build replaces homeport’s.
Binary or Output folder
For an app, Binary is the file the build makes, which runs as your app. For a static site, Output folder is the folder the build leaves the site in. Either is a path inside the app’s folder. Setting it turns off the fallback that looks for a site in build, dist or out, and an rsc-kit app’s own marker.
Runtime
For a JavaScript app: Node or Bun. Left on Detected, it is what the project says, read in the order on the detection page, else Node. It isn’t shown when the build compiles the server into a binary, which runs on the Bun it was compiled with.
How it runs
The start command, release command and processes are all arguments to your app’s binary, not commands for a shell. For Laravel the binary is FrankenPHP, so artisan is php-cli artisan …. The characters each may use are listed under run and release in the homeport.yaml reference, and each is at most 1,000 characters.
Start command
The arguments your binary starts with, like serve --port $PORT. Only $PORT and $HOST may be used. Empty, it is what was detected: for a JavaScript app, what its start script runs; for a PHP app, php-server --root public --listen :$PORT, or Octane when it is on.
Release command
Runs once against the new release before it takes traffic, typically database migrations. If it fails, the deploy stops and the previous release keeps serving. Write the arguments only: migrate up, not ./server migrate up. No variables, so no $, and no &&.
When neither the build settings nor homeport.yaml set one, homeport detects it:
| If the app is | The release command |
|---|---|
| Laravel | php-cli artisan migrate --force |
rsc-kit, with src/commands/migrate.ts (or .tsx, .js, .mjs, .mts) | migrate |
Node or Bun, with a migrate or db:migrate script in package.json | What that script runs, as arguments to the runtime |
Node or Bun, with Prisma’s prisma/migrations folder | node_modules/prisma/build/index.js migrate deploy |
| Anything else | None |
- A script that runs
prismaordrizzle-kitis run from its file undernode_modules. The release shipsdependenciesonly, so the tool must be independencies, notdevDependencies. - A script that needs a shell (
&&, a variable) can’t be a release command. homeport says why in the build’s warnings, and you set the release command yourself. - For a Laravel app, homeport also caches its views as part of the release command.
- To have no release command where one would be detected, write
release: nonein homeport.yaml.
Octane
Laravel apps only. Detected serves the app through Laravel Octane, in FrankenPHP’s worker mode, when it requires laravel/octane. On needs laravel/octane in composer.json. Off serves it with php-server. A start command you set wins over this switch.
Processes
Up to 4 long-running commands beside the web, like a queue worker (php-cli artisan queue:work). Each has a name and its arguments to the binary. A name is lowercase letters and digits, starting with a letter, up to 15 characters, and not web or release. A process may use $PORT and $HOST, and gets a port of its own.
- Each runs once, beside however many copies of the web are running, and is billed for its memory while it runs.
- Memory and CPU are the process’s own. Empty, it runs at the app’s size. A process can’t be larger than any of the app’s environments: make the app bigger in Resources first.
- Processes run only in an environment with at least 1 copy always running, which needs a paid plan. An environment that sleeps skips them at each deploy. See always on.
Health check path
A deploy goes live once the app answers this path with a status below 400. Empty, it is /. Set it to a path that answers without signing in, like /api/health. If the app doesn’t answer in time, the deploy is rolled back: see what your app must do.
It is a path on the app: it starts with /, has letters, digits and . _ - / only, no query, no .., and is at most 200 characters. Unlike the other settings, each environment has its own.
Size
An app’s memory and CPU are not build settings. They are set per environment in Settings → Resources, and saving restarts the app without a rebuild. See sizes. A process with no size of its own runs at the environment’s size.
Packages
Executables from homeport’s curated list, shipped inside each release and on your app’s PATH. Nothing is installed on a server: the release carries them. Each adds its size to every release.
| Package | Version | What it is | Size | Licence |
|---|---|---|---|---|
lightpanda | 1.0.0 | Lightpanda, a headless browser for scraping and automation, driven over the Chrome DevTools Protocol | About 190 MB | AGPL-3.0 |
- Up to 8 packages, each named once.
- Static sites can’t have packages: nothing of them runs.
- Choosing any in the dashboard replaces the ones homeport.yaml names. While none is chosen, the dashboard shows homeport.yaml’s.
Start Lightpanda from your app and drive it over localhost, with puppeteer-core or Playwright:
packages:
- lightpandalightpanda serve --host 127.0.0.1 --port 9222