# Builders

> Run and manage Falak builders — the control plane host builder, builder servers and external falak-builder workers — with tokens, timeouts and fixes.

Source: https://falak.sh/docs/operations/builders/

A **builder** turns a commit into an artifact or image. Every installation has one; add more for Docker builds or more capacity.

![Settings → Builders: the control plane builder and registered builders with their status, modes and last poll.](./_images/settings-builders.png)

## Kinds of builders

| Builder | Setup | Modes | Serves |
|---|---|---|---|
| Control plane host (`builder` service, name `control-plane`) | Automatic | `native` (`FALAK_LOCAL_BUILDER_MODES`) | Every organization |
| Builder server (type `builder`) | Create a server of type **Builder**; `falak-builder` is installed and configured when it finishes provisioning | `native`, `docker` (with Docker) | Its organization |
| External builder | **Settings → Builders → New builder**, then run `falak-builder serve` anywhere | as configured | Its organization |

A builder counts as online when it polled within the last 120 seconds. Managing builders needs `builds.manage`.

## Run an external builder

1. Create the builder in **Settings → Builders** and copy its token (`kbt_…`).
2. On the machine (Linux amd64/arm64 with git and, for Docker builds, Docker with Buildx):

   ```bash
   curl -fsSL -o /usr/local/bin/falak-builder https://falak.example.com/install/builder/linux-amd64
   chmod +x /usr/local/bin/falak-builder
   FALAK_URL=https://falak.example.com FALAK_BUILDER_TOKEN=kbt_… FALAK_BUILDER_NAME=build-1 \
     falak-builder serve
   ```

3. Run it under systemd or another supervisor so it restarts.

### Docker builds on an external builder

An external builder in Docker mode pushes images to Falak's [built-in registry](/docs/operations/install/#the-built-in-image-registry) at `https://registry.<your domain>`. Falak sends the registry address and credentials with each job, so there is nothing to log in to, but the machine must resolve and reach that host over HTTPS, and its Docker must trust the certificate (the default Let's Encrypt one is; with `--tls internal` it is not). Builder servers work the same way.

### `falak-builder` commands

```text
falak-builder run [--job FILE|-]     run one job (JSON), stream NDJSON events to stdout
falak-builder serve                  poll the control plane for jobs (FALAK_URL + FALAK_BUILDER_TOKEN)
falak-builder detect [DIR]           print the detected build plan (--dockerfile: the generated Dockerfile)
falak-builder version
```

| Flag | Env | Default |
|---|---|---|
| `--url` (serve) | `FALAK_URL` | required |
| `--token` (serve) | `FALAK_BUILDER_TOKEN` | required |
| `--name` (serve) | `FALAK_BUILDER_NAME` | hostname |
| `--wait` (serve) | | `30` s long-poll |
| `--once` (serve) | | exit after one job |
| `--work-dir` | `FALAK_BUILDER_WORK_DIR` | `<tmp>/falak-builder/work` (base: `FALAK_BUILDER_DIR`) |
| `--cache-dir` | `FALAK_BUILDER_CACHE_DIR` | `<tmp>/falak-builder/cache` |
| `--artifacts-dir` | `FALAK_BUILDER_ARTIFACTS_DIR` | `<tmp>/falak-builder/artifacts` |
| `--keep-workspace` | | off (debugging) |
| `--log-level` | `FALAK_LOG_LEVEL` | `info` |
| `--runtime` (detect) | | hint: `php`, `node`, `bun`, `deno`, `static` |

Two builder processes sharing one token must use **different names**. A poll by one would otherwise fail the other's running build ("Builder &lt;name&gt; restarted during the build.").

## Limits and timeouts

| Setting | Default |
|---|---|
| Build timeout | 1800 s (`FALAK_BUILD_TIMEOUT`), plus 120 s grace |
| Assigned but never started | re-queued after 180 s (3 attempts) |
| No heartbeat from a running build | failed after 90 s (`FALAK_BUILD_HEARTBEAT_TIMEOUT`) |
| Queued with no eligible builder | failed after 3600 s (`FALAK_BUILD_QUEUE_TTL`) |
| Build log retention | 30 days |

## Troubleshooting

| Symptom | Fix |
|---|---|
| Builds stay **queued** | `falak-ctl logs builder`. Docker-mode builds need a builder that accepts `docker`; the host builder does native only by default. |
| Docker build fails at push (`no such host`, `401`, `x509`) | The `registry.<domain>` record is missing or the install predates the registry: `falak-ctl up`, add the record, then `falak-ctl registry status`. `x509`: the builder doesn't trust the registry's certificate (`--tls internal`). |
| Build fails with `railpack detected …, which native builds do not support` | Use the Docker build mode for that stack. |
| `Builder <name> restarted during the build.` | The builder process restarted (for example during `falak-ctl update`). Redeploy. |
| Build is slow the first time | Caches are warm from the second build on (package manager and BuildKit caches). |

## Next steps
