Skip to content

How Deploys Work

Most deploy problems are easier to fix once you know which step they happened in. This page walks through what ZevCloud does between your push and your app answering requests.

push ──▶ match ──▶ clone ──▶ detect ──▶ build ──▶ start ──▶ route

1. Match. A push to your connected repository tells us which services to deploy. Only services on that repository and that branch are considered. If several services share one repository, we also compare the changed files against each service’s Root Directory, so a commit that only touches frontend/ doesn’t rebuild your API. Changes to shared files outside every service folder rebuild all of them.

2. Clone. We fetch that exact commit. If an image for this service already exists for the same commit, we skip straight to the start step — this is why redeploying an unchanged commit is near-instant.

3. Detect. We read your Root Directory and work out how to build it:

  • A Dockerfile there means you’ve already described your build, so we use it as-is. See Using a Dockerfile.
  • Otherwise we look for a manifest — package.json, requirements.txt, pyproject.toml, go.mod, Gemfile, composer.json — and hand the folder to Nixpacks, which works out the language, the system packages it needs, and how to install and start it. Its provider docs list exactly which files each language is detected by, which is the quickest way to check why your project was detected as one thing rather than another.

You can see the resolved plan at the top of any build log, including the detected language and the exact install and start commands.

4. Build. The plan runs in three phases:

PhaseWhat happensExample
SetupSystem packages your stack needsPython, Node, a compiler when a dependency requires one
InstallYour dependenciesnpm ci, pip install -r requirements.txt
BuildYour build command, if you have onenpm run build

5. Start. The new container runs your start command and has to answer on the configured Port before it receives traffic.

6. Route. Traffic moves to the new container and the previous one stops. Your domain and certificate carry over untouched.

Each phase is a layer, and layers are reused until something they depend on changes. Your source code is copied in last, so a code-only change reuses everything above it.

LayerRebuilt when
System packagesYour detected stack changes — a new dependency needs a compiler, you change runtime version
DependenciesYour manifest or lockfile changes
Your codeEvery commit (this is the cheap part)

The practical shape of this: the first build of a project is the slow one. A first build that takes several minutes is normal, and later builds of the same project are usually well under a minute. A build that’s slow every time means something is invalidating a layer on each run, and that’s worth looking at.

Work through these in order.

Compare durations in the Deployments tab. First build slow, subsequent builds fast is the system working correctly — there’s nothing to fix.

This is the most common cause of a build that’s slow every time. Some packages ship only source code, so your build has to install a compiler and build them on every machine that hasn’t done it before. Swapping to a prebuilt equivalent is usually a one-line change:

SlowFastLanguage
psycopg2psycopg2-binaryPython
mysqlclientpymysqlPython
bcryptbcryptjsNode
node-sasssassNode
sharp pinned to an old versioncurrent sharpNode

psycopg2 is worth calling out: it pulls in a C compiler and PostgreSQL development headers before it can build, which adds several minutes on its own. psycopg2-binary is a drop-in replacement that installs in seconds, and both SQLAlchemy and Django accept it without any code change.

When you create a service, ZevCloud flags these in your manifest before you deploy.

Is your lockfile changing on every commit?

Section titled “Is your lockfile changing on every commit?”

If your lockfile is regenerated or reformatted on each commit, the dependency layer can never be reused. Commit a stable lockfile and leave it alone between dependency changes.

Are you installing things you don’t need at runtime?

Section titled “Are you installing things you don’t need at runtime?”

Browser automation tools (Puppeteer, Playwright, Cypress) download 100–200 MB of browsers on install. If they’re in devDependencies and your build doesn’t run tests, they’re pure cost. ZevCloud sets variables to skip those downloads by default on Node projects.

Is the build doing work your app doesn’t need?

Section titled “Is the build doing work your app doesn’t need?”

Machine-learning frameworks (torch, tensorflow) are hundreds of megabytes. That’s a real cost you may need to pay, but it’s worth checking it’s actually imported.

Open Deployments → the failed deploy. Match the last thing in the log to a phase above:

Last thing you seePhaseUsually means
failed to detect the application typeDetectRoot Directory is wrong, or the folder has no recognised manifest
A package manager error, ERESOLVE, a compiler errorInstallA dependency problem — read the first error, not the last
Your own build command failingBuildReproduce locally with the same command
Build succeeded, deploy failed on health checksStartYour app isn’t answering on the configured Port
Deploy succeeded, every URL returns Bad GatewayRouteThe Port setting doesn’t match what your app listens on

The last two are the same root cause and are covered in detail under Troubleshooting.

Environment variables apply at build, not at restart. A variable change reaches your app on the next deploy. Restarting a running container won’t pick it up. The dashboard reminds you while you have unapplied changes.

A restart is not a redeploy. Restart reuses the existing image. Settings that affect how a container is built or routed — Port, build commands, variables — need a redeploy.

Build timeouts scale with your plan. A build that’s cut off mid-way is a timeout, not a crash. Limits are listed under Plans.

The Root Directory is also the auto-deploy filter. Setting it to a subfolder means pushes that don’t touch that folder won’t deploy. That’s usually what you want, and occasionally surprising when you expect a deploy and don’t get one.