Skip to content

Deployments

A deployment is what one build produced: a complete set of files, frozen. It is never modified after it is created, never rebuilt, and never overwritten by a later deploy.

Production is a pointer to one deployment. Promoting is moving the pointer, which is the whole reason rolling back takes a second rather than a build.

pending → queued → building → deployed
↘ failed
↘ cancelled
Status Meaning
pending Created, waiting for a build slot
queued Handed to the build system
building Cloning, installing, building, uploading
deployed Live and promotable
failed Something went wrong. The build log says what
cancelled Stopped before it could be promoted. See cancellation for what it costs

Only a deployed deployment can be promoted. A failed one has no files behind it.

A push to your project’s default branch builds and promotes automatically. Any other branch builds a preview instead and leaves production alone.

Promotion, whether automatic or one you triggered, repoints production at the new deployment and purges the edge cache so visitors stop getting the old one. No files move. The previous deployment is still there, still complete, still one API call from being live again.

After each build, deploybase inspects the output and decides what to serve when a request matches no file. There is nothing to configure, and because it is decided per deployment, changing the files changes the behaviour from that deployment on.

In your build output Served for an unmatched path
200.html 200.html, status 200
A _redirects file with a /* catch-all rewrite to /index.html at status 200 index.html, status 200
404.html 404.html, status 200
None of the above A plain 404 from the CDN

The edge rewrites the request to the fallback and serves that file, so the response carries the file’s own status. A 404.html delivered this way is a soft 404: it looks right to a visitor, and a search engine will index it as a real page. If that matters to you, and for a content site it does, prefer a real 404 to a 404.html you did not intend as a fallback.

When a deployment has both an SPA fallback and a 404.html, the fallback wins. This matches Netlify: a catch-all rewrite hands routing to your app, and your app decides what a missing route looks like.

SPA fallback behaves the same on production, preview and custom domain URLs. The soft-404 part does not: on preview URLs a 404.html fallback returns a real 404 status rather than a 200. If you depend on the status code, test it on the surface you actually serve from. The SvelteKit guide covers the framework side.

A _redirects file that fails to parse anywhere in the file disables fallback detection entirely, so a typo in an unrelated rule costs you the catch-all.

Other _redirects rules, including 301 and 302 redirects, arbitrary rewrites and force rules, are parsed and stored with the deployment but are not yet applied at the edge. Do not rely on them.

A _headers file in your publish directory sets response headers on the paths you choose. It uses the same syntax as Netlify and Cloudflare Pages, so a file you already have works unchanged:

/*
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; img-src 'self' https://cdn.example.com
/fonts/*
Cache-Control: public, max-age=31536000, immutable

A path pattern starts at column 0. The header lines under it are indented, one Name: value per line, and belong to that path until the next pattern. Lines starting with # are comments, and blank lines are ignored. Header names are case-insensitive.

Patterns must start with /. * matches anything and can appear anywhere in a segment, but only once per pattern, so /img/*.png works and /a/*/b/* is skipped. :name matches one segment and only at the start of one, so /blog/:slug works and /blog/post:slug does not. A single segment cannot use both.

Header values must be plain ASCII, so a curly quote, an em dash or an accented character pasted into a value gets that line skipped. Write -- or the HTML entity instead.

Rules are applied in file order, and a later rule replaces an earlier one for the same header, so put your broad /* block first and your specific overrides after it. Repeating a header inside one block joins the values with a comma instead, which is what you want for Link.

These are part of the response we construct, so a rule naming one is skipped: accept-ranges, age, allow, alt-svc, connection, content-encoding, content-length, content-range, date, etag, location, server, set-cookie, trailer, transfer-encoding, upgrade, cdn-tag, and anything starting with x-deploybase-.

Every skipped line is reported in the build log with its line number and the reason, along with anything else the file could not use: a malformed line, an invalid header name, a path with no headers under it, or Cloudflare’s ! Name detach syntax, which we do not support. One bad line never costs you the rest of the file. A file over 1 MB, or one with more than 100 path blocks, is not applied at all, and the build log says so.

X-Robots-Tag is yours to set. Preview and share-link URLs keep their own noindex regardless, so a rule that makes a page indexable on production does not expose your previews to search engines.

A Cache-Control you set here is the one the CDN caches by, not just the one browsers see. The default is a short browser lifetime with a long edge lifetime, and a deployment going live purges the edge, so visitors get new content immediately without you configuring anything. Setting your own Cache-Control replaces that default for the paths it matches. If you want a different lifetime at the edge than in the browser, set s-maxage alongside max-age.

The long immutable cache in the example above is safe for content-hashed filenames, where a change produces a new filename. Applied to a path whose filename stays the same, it means visitors keep the old file for a year, and no deploy and no purge can recall it.

Nothing expires them. No cleanup job deletes a deployment, so every build you have made is still there and still promotable, however old.

They do go when you remove them deliberately: deleting a deployment, a project or a team removes the deployments underneath it.

We expect to introduce a retention policy. When we do, we will publish the rule here and tell affected teams before it takes effect, because a deployment quietly disappearing would break exactly the rollback guarantee this page is built on.

Build logs are kept with the deployment and are not pruned either. The internal build-job records are cleaned up after 30 days, which does not affect the log you can read through the API.

Terminal window
# Recent deployments for a project
curl "https://api.deploybase.eu/api/v1/deployments?project_id=$PROJECT_ID&status=deployed" \
-H "Authorization: Bearer $DEPLOYBASE_API_KEY"
# One deployment
curl "https://api.deploybase.eu/api/v1/deployments/$DEPLOYMENT_ID" \
-H "Authorization: Bearer $DEPLOYBASE_API_KEY"
# Its build log
curl "https://api.deploybase.eu/api/v1/deployments/$DEPLOYMENT_ID/logs" \
-H "Authorization: Bearer $DEPLOYBASE_API_KEY"

Logs exist from the moment a build starts, so building, deployed and failed deployments all have one. Builds are asynchronous: trigger, then poll every five seconds or so.

Live a8b7eb1d built