Every deploy starts with a build. dply clones your repository at the commit being deployed, installs dependencies, runs your build command in an isolated container, and uploads the output directory to the edge. This page covers the build settings, how dply picks a Node version and package manager, the build cache, and the limits your plan sets on build time and concurrency.
Container apps build differently: dply builds a Docker image from your repository instead of running a Node build. See Container apps.
Build settings
In your app, open Build. The page opens with one sentence, such as "Every push to main on acme/web runs npm ci && npm run build and publishes dist/. The last 10 releases are kept for rollback." Below it, under How it builds, each setting is a row; click one to change it in a dialog and choose Save.
| Row | What it does | Default |
|---|---|---|
| Deploy on push | Whether a push to the production branch starts a deploy. See Deploy triggers & hooks. | On |
| Build command | The shell command that produces your site. Required, up to 500 characters. | npm ci && npm run build |
| Output folder | The folder, relative to the build folder, that dply uploads after the build. Required. | dist |
| Repository root | A subdirectory to build from, such as apps/web. See Monorepos. |
The whole repository |
| Unknown paths | Serve index.html for unknown paths so client-side routers work, or return 404. Not shown for server-rendered or container apps. A change updates the live deployment immediately. |
Serve index.html |
| Releases to keep | How many past deployments keep their files for rollback, from 1 to 50. Older deployments stay in the history but lose their files. | 10 |
| Deploy id in the footer | Prints the live deployment id at the bottom of HTML pages. | Off |
Other settings apply to the next deploy, so redeploy afterwards: open Deploys and choose Redeploy the latest. The repository and branch are shown at the top of the list and can't be changed here.
A dply.yaml file in your repository can override the build command, output directory, and build root on every deploy. When it does, the row shows the file name in amber, the setting's dialog says what the file sets, and a row for the file lists its build keys and any warnings. See Configuration files.
Note
The repository and production branch are fixed when you create the app. To build from a different repository or branch, create a new app.
What a build does
Each build runs these steps, and the live log on Deploys shows each one as it happens:
- Clone. dply clones the repository at the commit being deployed, using your linked Git account for a private repository (the token never appears in the log). When a Repository root is set, only that folder and the workspace files it needs are checked out. See Source control.
- Read configuration. dply reads
dply.yamlandwrangler.tomlfrom the build directory. Adply.yamlthat fails validation stops the build here. - Restore the build cache. See Build cache below.
- Install and build. dply installs dependencies with the package manager your lockfile names, then runs your build command.
- Check the output. The output directory must exist and, for static and hybrid sites, contain an
index.htmlat its root. The total output must be under 500 MB. - Publish. dply uploads the output and switches traffic to the new deployment. See Deployments.
Node.js version
dply picks the Node.js version from your repository, checking these in order and using the first one it finds:
engines.nodeinpackage.json.nvmrc.node-versionpackageManagerinpackage.json(for example,pnpm@11implies Node 22)
Supported versions are Node 18, 20, 22, and 24. With no version hint, dply uses Node 22. A few rules decide the final version:
- An open-ended range such as
>=18builds on Node 22, not 18. Many starter templates declare>=18while their dependencies need newer Node. - An odd-numbered release such as 21 rounds up to the next supported version (22).
- A version below 18 builds on 18, and a version above 24 builds on 24.
lts/*aliases in.nvmrcare ignored.
The build log shows which version was chosen and why:
[node] Detected Node 20 from .nvmrc ("20") — using node:20-bookworm
Note
Only the four sources above set the Node version. A node key under build: in dply.yaml, or a NODE_VERSION variable, has no effect.
Package managers
dply installs dependencies for you, based on the lockfile in the build directory:
| Lockfile | Install command |
|---|---|
pnpm-lock.yaml |
pnpm install --frozen-lockfile, through Corepack, with a pnpm version matched to the Node version |
yarn.lock |
yarn install --frozen-lockfile, through Corepack (the packageManager version when set, otherwise Yarn 4) |
bun.lock or bun.lockb |
bun install --frozen-lockfile |
package-lock.json |
npm ci |
package.json only |
npm install |
Because dply runs the install step itself, it adjusts your build command in three ways:
- A leading install step, such as
npm ci &&orpnpm install &&, is removed so dependencies are not installed twice. npm runandnpm execare rewritten to the detected package manager, sonpm run buildruns aspnpm run buildin a pnpm repository.--if-presentis added to eachrunscript.
Important
Because of --if-present, a missing build script does not fail the install step. The build fails later with Build output directory not found or Build output is missing index.html. If you see either error, check that the script named in your build command exists in package.json.
Hooks
Under Build → Hooks you can add commands that run around the build, one per line. Static, SSR and hybrid apps get Before the build (after dependencies install) and After the build (before publishing); they run in the same container and appear in the build log. Container apps get Release, which runs in the new version before it takes traffic, and After deploy, which runs once the deploy is live. See dply.yaml hooks to set them in your repository.
Environment variables during the build
The build container receives your production environment variables, the secrets linked to the app, any env.public values and build.env_files from dply.yaml, and variables from attached resources such as Realtime. Frameworks that inline variables at build time, like Vite's VITE_*, read them here. See Environment variables.
Build cache
dply caches dependencies and framework caches between builds of the same app:
node_modulesandnode_modules/.cache.next/cache,.nuxt,.astro,.svelte-kitdist/.cache
The cache key combines the Node version, the repository root, and your lockfile. When the lockfile changes, dply restores the newest cache built on the same Node version, and your package manager reconciles the difference. Each app keeps up to 500 MB of cache, and the oldest entries are removed first. The cache is saved after the deployment is published, so it does not slow the deploy down.
Separately, downloaded npm, pnpm, and Yarn packages are cached per organization, and each repository's Git history is kept between builds, so repeat clones fetch only new commits.
The cache never blocks a deploy. If a restore or save fails, the log records it and the build continues cold.
Note
There is no control to clear the build cache. A lockfile change starts a fresh cache key. To force a cold install, change the lockfile.
Limits
Build concurrency and timeout depend on your organization's plan:
| Starter | Pro | Team | |
|---|---|---|---|
| Sites | Unlimited | Unlimited | Unlimited |
| Concurrent builds | 1 | 2 | 5 |
| Build timeout | 20 min | 45 min | 60 min |
| Custom domains (per organization) | 3 | 20 | 100 |
| Container app instances | 1 per app | Autoscaling | Autoscaling |
| Queue workers per app | 1, starts when jobs arrive | 5, autoscaling | 10, autoscaling |
| SQL databases (D1) | 2 | 10 | 50 |
| Queues | 2 | 10 | 50 |
| Realtime connections per app | 200 | 1,000 | 5,000 |
| Audit log | No | No | Yes |
| Meter | Price | Unit |
|---|---|---|
| Build time | $0.0091 | per minute, billed per second |
Each plan includes build minutes every billing period: 300 on Starter, 1,000 on Pro and 3,000 on Team. Build time past that bills per second. See Plans & pricing.
How these work:
- Build time is counted, per second, from the start of the clone to the end of the build. Time spent waiting in the queue and publishing is not counted. Failed builds count too.
- Concurrency is per organization. When every slot is busy, the next build waits and its log shows
Queued — waiting for an earlier build in this organization to finish. - Timeout stops a build that runs longer than your plan allows. The deploy fails and the live site keeps serving the previous deployment.
- Superseded builds. Starting a new deploy of an app cancels any build of that app that is still running. When you push several commits quickly, only the newest one finishes.
Each build container also has fixed resources: 4 GB of memory, 2 CPUs, and a limit of 2,048 processes.
Build isolation
Your build runs code from your repository and your dependencies, so dply isolates it from the platform and from other customers:
- Each build runs in its own container, as an unprivileged user, with all Linux capabilities dropped and privilege escalation blocked.
- Builds run on a dedicated network, and cloud metadata hostnames resolve to nowhere.
- Package download caches are separate for each organization, and the build cache is separate for each app, so one customer's build cannot change what another customer's build installs.
- The build receives only your app's own environment variables and linked secrets.
For more on how dply separates customers, see Platform security & isolation.
Troubleshooting
| Log message | What to check |
|---|---|
Build output directory not found: dist |
The Output folder does not match what your framework writes, or the build script did not run. |
Build output is missing index.html at the root of: dist |
Point the Output folder at the folder that contains index.html, or use server rendering for apps without one. |
Build artifacts exceed maximum allowed size. |
The output is over 500 MB. Move large media to object storage. |
dply config lint failed: … |
Fix the listed errors in dply.yaml. See Configuration files. |
Repository root "apps/web" was not found in the checkout. |
Correct the Repository root row on Build. |
For more, see Troubleshooting builds.