# Build modes

> Choose between Falak's native builder (release tarballs) and Docker builds (images in Falak's registry), what each needs, and how to enable Docker builds.

Source: https://falak.sh/docs/guides/build-modes/

Every Git-backed site has a **build mode** that decides what the builder produces. Pick it under **Settings → Build → Build mode**.

## Compare

| | Native | Docker |
|---|---|---|
| API value | `native` | `docker` |
| Output | A `tar.gz` release unpacked into `releases/<id>` | An image `<registry>/<namespace>/<slug>:<build-id>` |
| Runtimes | frankenphp, php-fpm, node, bun, deno, static | docker, compose |
| Runs on the server as | Files served by Caddy/FrankenPHP/PHP-FPM, or a supervised process | A container (blue/green) or a Compose project |
| Needs | Any builder (the control plane builder works) | A builder with Docker: a `builder` server, or the host builder with `docker` enabled |
| Rollback | Switch `current` back (instant) | Start the previous image |
| Processes tab (workers, cron) | Yes | No |
| Default for | PHP, JavaScript and static presets | Docker preset |

A third mode, **on-server** (building on the target server), exists in the data model but is **not supported yet**; deployments with it fail immediately with a clear error.

## When to choose Docker

- Your app needs system packages that Falak's servers do not have.
- You already maintain a `Dockerfile`.
- You deploy a multi-container app ([Compose](/docs/deploy/docker-compose/)).

Otherwise native is faster, needs no registry, and gives you Falak-managed processes and zero-downtime symlink switches.

## Enable Docker builds

The builder on the control plane host accepts only native jobs by default. You have two options:

1. **Recommended: add a builder server.** Create a server of type **Builder** with Docker. When it finishes provisioning, Falak installs `falak-builder` on it with its own token. It then takes Docker (and native) jobs for your organization.

2. **Or let the host builder build images.** Only when the host builder can reach a Docker daemon with BuildKit: set `FALAK_LOCAL_BUILDER_MODES=native,docker` on the control plane (in `/opt/falak/custom.env`), then `falak-ctl up`. The stock `builder` container has no Docker daemon, so this needs your own setup.

Builds that no builder can take stay **queued** and fail after `FALAK_BUILD_QUEUE_TTL` (3600 s).

## Falak's registry

Docker builds push to Falak's **built-in registry**, which the installer sets up at `https://registry.<your domain>` (basic auth, credentials generated into `.env`). Builders push there and servers pull from it, pinned by digest; Falak hands both the credentials. Create the `registry.` [DNS record](/docs/operations/dns/) before your first Docker build.

| Variable | Default |
|---|---|
| `FALAK_REGISTRY_URL` | `registry.<domain>` (`registry.falak.local` in the simulation) |
| `FALAK_REGISTRY_NAMESPACE` | `falak` |
| `FALAK_REGISTRY_USERNAME`, `FALAK_REGISTRY_PASSWORD` | generated by the installer |

Old images are deleted daily and their layers freed by a weekly garbage collection. See [The built-in image registry](/docs/operations/install/#the-built-in-image-registry).

With `--tls internal`, Docker on builder servers and app servers doesn't trust the registry's certificate. Docker builds need `--tls acme`, or Caddy's internal root trusted in each Docker daemon.

## Next steps
