Build configuration

We read your repository, work out what it is, and build a container image for it. Most repositories need no configuration; when yours does, a Dockerfile gives you full control.

How your app is detected#

Detection looks at the files in your app’s root directory. The first match wins, in this order:

OrderIf the root containsBuilt as
1DockerfileYour Dockerfile, exactly as written. See below.
2hugo.toml, hugo.yaml or hugo.jsonHugo, built with hugo --minify and served as a static site
3composer.jsonPHP: Laravel, Symfony, or plain PHP
4package.jsonA Node framework — see Node frameworks
5requirements.txt, pyproject.toml or PipfilePython: Django, FastAPI, Flask, or a script
6Cargo.tomlRust
7go.modGo
8GemfileRuby, or Rails
9pom.xml or build.gradleJava, with Maven or Gradle
10index.htmlA static site, served as it is

If nothing matches, the deployment fails with framework_undetected. Add a Dockerfile to build anything else.

Node frameworks

For a package.json, the framework is read from your dependencies, in this order. The build step runs your build script, if you have one.

DependencyFrameworkRuns as
nextNext.jsA server, with next start. See below for output modes.
nuxtNuxtA server
@remix-run/nodeRemixA server
@sveltejs/kitSvelteKitA server
gatsbyGatsbyA static site
@docusaurus/coreDocusaurusA static site
astroAstroA server if output is server or hybrid, otherwise a static site
@angular/coreAngularA static site
viteVite (React or Vue)A static site
react-scriptsCreate React AppA static site
express, fastify, koa, @nestjs/coreNode serverYour start:prod or start script
anything else, with a start scriptNodeYour start script

Next.js output modes

  • Default: runs next start.
  • output: 'standalone': runs the generated server.js, with .next/static and public copied alongside it. Smaller and faster to start.
  • output: 'export': served as a static site from out/. There is no server.

The port your app listens on#

Your app receives its port in the PORT environment variable. Listen on it, on all interfaces (0.0.0.0), not only localhost.

RuntimePORT
Node, Ruby3000
Python8000
Go, Rust, Java, PHP, static sites8080
Your own DockerfileThe last numeric EXPOSE line, or 3000 if there is none

A deployment is ready when something accepts connections on that port. If nothing does within four minutes, it fails with rollout_failed.

Node.js#

Version

Supported major versions are 18, 20, 22 and 24. The default is 22. Set engines.node in package.json to choose another; ranges such as >=20 and ^18 are understood, and the newest supported version that satisfies yours is used. A .nvmrc is read only when engines.node is absent.

package.json
{
  "engines": { "node": "20.x" }
}

Package manager

Chosen from your lockfile:

LockfileInstall command
pnpm-lock.yamlpnpm install --frozen-lockfile
yarn.lockyarn install --immutable
bun.lock or bun.lockbbun install --frozen-lockfile
package-lock.jsonnpm ci
nonenpm install

Development dependencies are installed, so your build tools are available. Workspaces and monorepos are supported; see Root directory.

Other runtimes#

RuntimeVersionWhat we run
Python3.12Installs requirements.txt, or the project from pyproject.toml. Django runs under gunicorn, FastAPI under uvicorn, Flask under gunicorn; otherwise python main.py. One of manage.py, app.py, main.py, wsgi.py, asgi.py, application.py, server.py or run.py must be in the root directory.
Go1.23Builds the first main package in the module and runs it.
RuststableRuns cargo build --release --locked and starts the binary it produces.
Ruby3.3Rails runs rails server; otherwise ruby app.rb.
PHP8.3Apache, serving public/ if public/index.php exists, otherwise the root. Dependencies installed with Composer.
Java21Builds with Maven or Gradle, skipping tests, and runs the jar.

These are the defaults we generate. To use a different version or command, bring your own Dockerfile.

Your own Dockerfile#

If the root directory contains a file named exactly Dockerfile, it is built as written and nothing is detected. This is how to deploy any language, version or start command we do not generate.

Environment variables reach your Dockerfile build in two ways; see Environment variables.

Root directory#

For a monorepo, set Settings → Source → Root directory to the folder that contains the app, such as apps/web. Detection, the build and the start command all run from there. The whole repository is still fetched, so the build can use shared code from other folders when your tooling supports it.

If the app brings its own Dockerfile and needs files from outside its folder — a shared package, a lockfile at the repository root — turn on Include files outside <your root directory> under Settings → Build. The build then runs from the top of the repository, using the Dockerfile in your root directory. It has no effect on Dockerfiles we generate.

Build resources#

Each build runs on its own machine with 2 vCPUs and 4 GB of memory, which is discarded when the build ends. Nothing is shared between builds, so every build installs dependencies from scratch. A build that runs longer than 20 minutes is stopped and fails with build_timeout.

Something missing or wrong on this page? Tell us, and quote the page title.