Skip to main content

How the image is built

The platform builds your application's image from a Dockerfile in the deployed folder. It is the only build path today, and the interface says so rather than offering fields that change nothing.

What Zero expects from the repository​

ItemRule
DockerfileAt the root of the deployed folder. Without it, the deployment fails at Building
FolderThe one the service declares inside the project's repository. Left blank, the repository root
Branch or tagThe one in the project's source, or the one given for that deployment
PortThe application must listen on 0.0.0.0, on the port given by the PORT variable

What the platform does not do​

  • It does not detect frameworks. There is no "we detected Node and configured it for you".
  • It does not run buildpacks. There is no build without a Dockerfile.
  • It accepts no configurable build command. What builds is the Dockerfile.

This is stated here and on the screen itself, because a form with those fields would create the expectation that filling them in changes something.

A minimal Dockerfile that works​

FROM node:22-alpine
WORKDIR /app

COPY package*.json ./
RUN npm ci --omit=dev

COPY . .

# The platform supplies PORT (default 8080). The server reads process.env.PORT
# and listens on 0.0.0.0. EXPOSE does not change the port, and USER is not needed.
CMD ["node", "server.js"]

What most often makes a deployment fail after the build is the port: the application has to read PORT from the environment and listen on 0.0.0.0, rather than hard-coding a number the platform does not know about. EXPOSE in the Dockerfile does not set the port.

How the application runs​

The platform runs every service under the same contract, whatever the image:

ItemWhat it is
UserAlways non-root, with numeric UID and GID 10001, whatever the image's USER
File systemRead-only, except /tmp
/tmpWritable, with a 512 MiB limit
HOMEPoints to /tmp
PortThe platform supplies the PORT variable with the service's port — default 8080. Listen on 0.0.0.0 on that port
ReadyThe instance accepts TCP connections on the service's port

Practical consequences:

  • The image does not need to declare USER. An image without USER — like most official language images, node:18-alpine for example — and an image with USER by name, such as USER node, work without changes.
  • Temporary files and caches go in /tmp. The rest of the file system does not accept writes while the application runs.
  • The readiness check is TCP, not HTTP. The platform does not know which path your application answers, so it does not ask for one: accepting connections on the port is enough.
  • Frameworks that follow the PORT convention work with no configuration — Express and Node with process.env.PORT, serve, gunicorn, Rails, Spring with server.port=${PORT}.

When the application does not become ready, the deployment fails with the reason and the previous version keeps answering. See Deployment states.

Monorepo​

Each service declares the folder it builds inside the project's repository — apps/api, for example. When creating the project, that is the Folder inside the repository field. The build happens from there, and that is where the Dockerfile has to be.

The declared folder carries over to every later deployment. A deployment that omitted it would look for the Dockerfile at the root of a repository that keeps it somewhere else.

The build log​

The builder's full output is recorded in both outcomes — success and failure. Recording only on failure would be the obvious and wrong choice: someone investigating a slow build, or one that produced the wrong image, needs the log of a build that succeeded.

You find it under Logs → Build, choosing the deployment. Credentials appearing in the output are protected before it is stored.

The artifact​

The build's result is an immutable artifact, identified by a unique fingerprint. It is never rebuilt in order to be reused: promoting a version to another environment reuses exactly the same artifact. See How a deployment works.

Common errors​

SymptomCauseWhat to do
Dockerfile: no such file or directory in the build logThe deployed folder has no DockerfileFix the service's folder
Authentication failure fetching the codePrivate repository with no access tokenEnter a token under Source, in the project
The build passes and the deployment fails with "The application started but did not answer"The application does not listen on 0.0.0.0 on the PORT port — it listens on localhost or on a fixed port, for exampleRead the port from PORT and listen on 0.0.0.0
The build passes and the deployment fails with "The application stopped right after starting"The application exits at startup — a missing variable, an unconfigured dependency, or a write outside /tmpLook at the last lines under Logs → Runtime
Dependency error during the buildA difference between your machine and a clean buildPin versions; the build does not reuse what exists on your machine

Next steps​