# Builds

Every deploy starts with a build: the commit is turned into either a folder of static files or a container image. This page is what happens inside that step.

## The steps

A build runs on Cloud Build and streams its progress into the deploy record:

1. **Fetch.** The commit is checked out. Apps with a [root directory](/create/application-settings#root-directory) build from that folder.
2. **Install.** Dependencies are installed with the [install command](/create/build-settings#install-command) - by default, the package manager your lockfile implies.
3. **Build.** The [build command](/create/build-settings#build-command) runs. For containers this happens inside the image build.
4. **Package.** A static build's [output directory](/create/build-settings#output-directory) is uploaded to object storage; a container image is pushed to Artifact Registry.
5. **Release.** The CDN starts serving the new files, or Cloud Run shifts traffic to the new revision.

Watch it live in the [build logs](/platform/observability#build-logs); each deploy keeps its full log.

## Dockerfiles: generated or your own

A `Dockerfile` in the repository **always wins** - the app builds and runs exactly as written, whatever is inside. See [Deploy with a Dockerfile](/platform/frameworks/docker).

Without one, Light Cloud generates a Dockerfile for the detected framework:

- Generated Node images run on **Node 22**.
- The install step follows your lockfile - `pnpm-lock.yaml`, `yarn.lock`, or `package-lock.json` decide the package manager, and with a lockfile present installs are exact (`npm ci` and equivalents).
- The image runs your server on the configured [port](/create/build-settings#port), with `PORT` set in the environment.

## Environment variables at build time

- **Static builds** run the build command with the environment's [variables](/platform/environment-variables#build-time-and-runtime) set - inlined values (`NEXT_PUBLIC_*`, `VITE_*`) work as usual.
- **Container images** are built *without* your runtime variables. Read configuration at startup, not at image build time - the same image may serve an environment whose variables have changed since.

## When a build fails

The deploy is marked failed and nothing is released - the previous deploy keeps serving. The lines that broke the build are pulled out of the build output and put on the deploy record, so the reason is on screen without digging.

Common causes, in the order they usually bite:

| Symptom | Usual cause | Fix |
| --- | --- | --- |
| Install step fails | Lockfile out of sync with `package.json` | Reinstall locally, commit the lockfile |
| Build command fails | Type errors, missing build-time variable | Read the extracted lines; set the variable on the [environment](/platform/environment-variables) |
| Output directory empty | Framework writes somewhere else | Match [output directory](/create/build-settings#output-directory) to your config |
| Green build, unreachable app | Server listens on the wrong port | Match the [port](/create/build-settings#port) or read `PORT` at startup |

A failed build costs its [build minutes](/billing/billing#metering) and nothing else.

## Related

- [Build settings](/create/build-settings): The four fields these steps read.
- [Rollbacks](/platform/deployments/rollbacks): Shipping again after a fix.
- [Observability](/platform/observability): Build logs and runtime logs, side by side.
