Build Netlify
KINDAreplaces $20/mosaves $240/yrback to the verdict
A hosting platform whose defining property is that a deploy is atomic: a site is never half old and half new, every deploy stays addressable forever, and rolling back is repointing rather than rebuilding.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the control plane, the serving layer and the build orchestrator.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree locally
Why Deploy history, manifests and domain state.
Get it Docker or your package manager. open ↗
Verify
psql connects and creates a table - installfree
Why Builds execute arbitrary code from a repository and running them on the host is not something to do even once.
Get it Docker or an equivalent, with a plan for network and filesystem restriction per build.
Verify
A build container cannot read the host filesystem or reach an unexpected network address - accountpennies per month at small scale
Why Blobs are stored once per distinct hash and referenced by many deploys, which is what makes keeping every deploy affordable.
Get it Any provider with cheap storage and a lifecycle policy for cleanup.
Verify
A file uploads by hash and is retrievable by that hash - have readythe price of a domain
Why Certificate issuance, apex handling and renewal cannot be tested without a real domain and real records.
Get it Any domain where you can change records, ideally with a subdomain free for testing.
Verify
A record change propagates and a certificate issues successfully
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- sites(id, name, production_deploy_id, custom_domains_json, build_config_json) - deploys(id, site_id, branch, commit_sha, state, manifest_id, built_at, build_log_path, context) · context is production, branch or preview - manifests(id, deploy_id, entries_json, entry_count, total_bytes) · path to content hash, immutable - blobs(content_hash, storage_key, bytes, first_seen_at, refcount) · one copy per distinct file - domains(id, site_id, hostname, certificate_id, verified_at, primary) - redirects(deploy_id, ordinal, from_pattern, to_target, status, conditions_json) · part of the deploy, not of the site - A redirect belongs to a deploy rather than to a site, because rolling back must roll back the routing rules too.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_URLsecret | required | postgres://localhost:5432/hosting | Sites, deploys and domain state. |
BLOB_BUCKET | required | s3://deploy-blobs | Content-addressed files, shared across deploys. |
BUILD_TIMEOUT_SECONDS | required | 900 | Hard. A build that never finishes blocks the queue behind it. |
PREVIEW_INDEXABLE | required | never | Present as a constant. Indexed previews are found months later in search results. |
DEPLOY_RETENTION | required | forever | Permanent deploy addresses are what make rollback a pointer move. |
CERT_RENEW_DAYS_BEFORE | required | 30 | Early, with alerting on failure. An expired certificate blocks the site entirely. |
The build, in order
Deploys that are all or nothing
A site is never partly updated, even mid-deploy.
Identical files cost one copy and rollback becomes a pointer move.
Files
src/blobs.tsUploading into a live directory is the half-updated page.
Files
src/manifest.tsDuring the switch, not before and after it.
Files
tests/atomic.test.ts
done when · tick each as it passeswatch out- Uploading files into a live directory means a visitor during the deploy gets new markup referencing assets that have not arrived yet, which manifests as a broken page for a few seconds and is nearly impossible to reproduce afterwards. Build the manifest first and switch to it atomically.
Rollback as a pointer move
Reverting to any previous deploy takes seconds and needs no rebuild.
Each at its own address, forever. It costs almost nothing.
Files
src/deploys.tsA rollback with the new redirects is a combination nobody tested.
Files
src/redirects.tsSeconds, not a rebuild. Measure it.
Files
src/rollback.ts
done when · tick each as it passeswatch out- Storing redirects and headers against the site rather than the deploy means a rollback restores the old files with the new routing rules, producing a combination that never existed and was never tested. Attach routing configuration to the deploy so a rollback is complete.
Running someone else's build safely
A build runs arbitrary code without reaching anything it should not.
A build runs arbitrary code from a repository.
Files
src/build/sandbox.tstests/build-isolation.test.tsA build that never finishes is a resource leak with a queue behind it.
Files
src/build/limits.tsLogs are read by people debugging and are the usual place secrets escape.
Files
src/build/logs.ts
done when · tick each as it passeswatch out- A build step runs whatever the repository's configuration says, which is arbitrary code, and a build container with platform credentials in its environment hands them to anyone who can open a pull request. Isolate per build, inject only that site's own declared variables, and never the platform's.
A preview per change
Every branch and pull request gets its own permanent address.
Predictable per branch and per pull request.
Files
src/previews.tsNot per site. Someone will forget and it is found months later.
Files
src/serve.tsWithout breaking permanent deploy addresses, which are the rollback path.
Files
src/cleanup.ts
done when · tick each as it passeswatch out- Preview deploys indexed by search engines put unreleased content and duplicate pages into search results, which is discovered months later by someone finding a staging page ahead of the real one. Serve previews with indexing disabled at the platform level rather than trusting each site to configure it.
Domains, certificates and going liveproduct builder
A custom domain works with a valid certificate and renews without intervention.
And re-verify periodically, since records change.
Files
src/domains.tsAn expired certificate is a site that browsers refuse to open.
Files
src/certificates.tsApex and subdomain differ and this is where users get stuck.
Files
docs/dns.md
done when · tick each as it passeswatch out- Automatic certificate renewal that fails silently produces a site presenting an expired certificate, which browsers block outright, and the first you hear is a customer telling you the site is down. Alert on renewal failure well before expiry and treat a failed renewal as an incident rather than a retry.
That is the whole plan for Netlify. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Edge functions.
- A global network.
- Forms, identity and platform conveniences.
- Multi-tenant scale and abuse handling.
- deploy previews
- CDN/edge functions
- forms
- rollbacks
- team workflows
- uptime/support
- Deploy notifications with the preview address, which is where the review workflow starts
- Split testing between two deploys, which the manifest model makes straightforward
- Build plugins with the same isolation guarantees, once the sandbox is proven
Need the files? The project pack on the verdict page hands your agent the whole brief · more hosting.