# Builds

> How Falak builds code once per deployment on a builder — stack detection, native tarball builds, Docker image builds, build-time variables and caching.

Source: https://falak.sh/docs/concepts/builds/

Every deployment of a Git-backed site starts with a **build**. Falak builds **once** per deployment, on a **builder**, and ships the result to every server. Your app servers never compile code, install Composer packages or run `npm install`.

## Where builds run

| Builder | How you get it | Build modes |
|---|---|---|
| **Control plane builder** | The `builder` service of every installation (`falak-builder serve`, name `control-plane`) | `native` only by default (`FALAK_LOCAL_BUILDER_MODES=native`) |
| **Builder server** | A server of type `builder`. `falak-builder` is installed automatically when it finishes provisioning. | `native` and `docker` (with Docker installed) |
| **External builder** | Created under **Settings → Builders**; run `falak-builder serve --url … --token …` anywhere | Organization-scoped |

A builder long-polls `GET /api/internal/builds/next` and takes the next job it is eligible for (organization and build mode). See [Builders](/docs/operations/builders/).

## Build modes

| Mode | API value | Output | Used by |
|---|---|---|---|
| **Native** | `native` | A deterministic `tar.gz` release (for example `vendor/`, `node_modules/`, built assets) | PHP, Node, Bun, Deno and static sites (default) |
| **Docker** | `docker` | An image pushed to Falak's registry: `<registry>/<namespace>/<site-slug>:<build-id>` | Docker and Compose sites |
| On server | `on-server` | Not supported yet: deployments fail fast with a clear error | |

## Stack detection (native builds)

The builder inspects the repository root and picks the first match:

| Found | Stack | Steps |
|---|---|---|
| `composer.json` | PHP (Laravel when `laravel/framework` is required and `artisan` exists) | `composer install --no-dev --optimize-autoloader --no-interaction --prefer-dist --no-progress`; then, if `package.json` has a `build` script, install JS dependencies and run `build` with `NODE_ENV=production`. `node_modules` is not shipped. |
| `deno.json` / `deno.jsonc` | Deno | `deno install` (`--frozen` with `deno.lock`); `deno task build` if defined |
| `package.json` | Node or Bun | Install, `build` script if present, prune dev dependencies |
| `index.html` | Static | none |
| `public/index.html` | Static (output `public`) | none |

**Package manager** (Node): `packageManager` in `package.json`, else `bun.lock`/`bun.lockb` → bun, `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, else npm.

| Package manager | Install | Prune |
|---|---|---|
| npm | `npm ci --include=dev` (with a lockfile), else `npm install --include=dev` | `npm prune --omit=dev` |
| pnpm | `pnpm install --frozen-lockfile` | `pnpm prune --prod` |
| yarn classic | `yarn install --frozen-lockfile` | `yarn install --production …` |
| yarn berry (`.yarnrc.yml`) | `yarn install --immutable` | (keeps all dependencies) |
| bun | `bun install` (`--frozen-lockfile` with a lockfile) | `bun install --production` |

**Versions** are read from your project: PHP from `config.platform.php` or `require.php` in `composer.json`; Node from your version files or `engines`; Bun from `packageManager`.

**Static output**: Vite (`dist`), Create React App (`build`) and Astro (`dist`, unless `@astrojs/node` is installed) produce static sites when there is no `start` script. With the static runtime, the first existing of `dist`, `build`, `out`, `public` is used.

When [Railpack](https://railpack.com) is installed on the builder, its detection enriches the plan (versions, start command).

## Overriding install and build commands

Set these **site variables** to replace the detected steps (both run with `sh -c`):

| Variable | Replaces |
|---|---|
| `FALAK_INSTALL_COMMAND` | The dependency install step |
| `FALAK_BUILD_COMMAND` | The build step |

```dotenv title="Site variables"
FALAK_INSTALL_COMMAND=pnpm install --frozen-lockfile --filter web...
FALAK_BUILD_COMMAND=pnpm --filter web build
```

See [Monorepos and build commands](/docs/deploy/monorepos/).

## Build-time variables

Builds do **not** see all your site variables. They see:

- variables whose names start with a public front-end prefix: `VITE_`, `NEXT_PUBLIC_`, `NUXT_PUBLIC_`, `PUBLIC_`, `REACT_APP_`;
- variables you **expose to the deploy script** (a per-variable opt-in in the Variables editor), for other build-time settings such as Astro's `SITE_URL`.

`${{ service.KEY }}` references in those variables are resolved before the build.

## What is excluded from artifacts

`.git`, `.DS_Store`, `/.env`, `/.env.*.local`, `/.falak-build`, `Thumbs.db` and `/storage/logs/*` (except `storage/logs/.gitignore`) are never shipped. PHP builds also exclude `/node_modules`.

## Docker builds

For `docker` mode, the builder chooses a Dockerfile in this order:

1. The Dockerfile path set on the site.
2. `Dockerfile` in the repository root.
3. A Railpack build plan, when Railpack is available.
4. A Dockerfile generated for the detected stack (printed in the build log).

Images are built with BuildKit and pushed to the [built-in registry](/docs/operations/install/#the-built-in-image-registry). Images no build or release needs any more are deleted daily, and a weekly garbage collection frees their layers.

## Caching and limits

| Setting | Default | Variable |
|---|---|---|
| Build timeout | 1800 s | `FALAK_BUILD_TIMEOUT` |
| Queued build expires if no builder takes it | 3600 s | `FALAK_BUILD_QUEUE_TTL` |
| Running build fails without a builder heartbeat | 90 s | `FALAK_BUILD_HEARTBEAT_TIMEOUT` |
| Artifacts kept per site | 10 | `FALAK_ARTIFACTS_KEEP` |
| Artifact maximum age | 90 days | `FALAK_ARTIFACTS_MAX_AGE_DAYS` |
| Maximum artifact size | 4 GiB | `FALAK_ARTIFACTS_MAX_BYTES` |
| Build log retention | 30 days | |

Package-manager and BuildKit caches persist in the builder's cache directory between builds. Clone credentials are fetched when a job is handed out and never stored.

If a builder restarts mid-build, the build fails with "Builder &lt;name&gt; restarted during the build." instead of hanging.

## Next steps
