Build Netlify

KINDAreplaces $20/mosaves $240/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

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.

estimated effort multi-daythe files for this build are in the project pack

StorageContent-addressed files with an immutable manifest per deployServingA manifest lookup per request resolving to content by hashBuildsIsolated build containers with a cache and a hard timeoutPreviewsA deploy per branch and per pull request, addressable separatelyRuntimeTypeScript with PostgreSQL and object storage

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. 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

  2. installfree locally

    Why Deploy history, manifests and domain state.

    Get it Docker or your package manager. open ↗

    Verify psql connects and creates a table

  3. 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

  4. pennies 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

  5. 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.

VariableNeededExampleWhere the value comes from
DATABASE_URLsecretrequiredpostgres://localhost:5432/hostingSites, deploys and domain state.
BLOB_BUCKETrequireds3://deploy-blobsContent-addressed files, shared across deploys.
BUILD_TIMEOUT_SECONDSrequired900Hard. A build that never finishes blocks the queue behind it.
PREVIEW_INDEXABLErequiredneverPresent as a constant. Indexed previews are found months later in search results.
DEPLOY_RETENTIONrequiredforeverPermanent deploy addresses are what make rollback a pointer move.
CERT_RENEW_DAYS_BEFORErequired30Early, with alerting on failure. An expired certificate blocks the site entirely.

The build, in order

  1. Deploys that are all or nothing

    A site is never partly updated, even mid-deploy.

    1. Identical files cost one copy and rollback becomes a pointer move.

      Files src/blobs.ts

    2. Uploading into a live directory is the half-updated page.

      Files src/manifest.ts

    3. During the switch, not before and after it.

      Files tests/atomic.test.ts

    done when · tick each as it passes
    watch 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.
  2. Rollback as a pointer move

    Reverting to any previous deploy takes seconds and needs no rebuild.

    1. Each at its own address, forever. It costs almost nothing.

      Files src/deploys.ts

    2. A rollback with the new redirects is a combination nobody tested.

      Files src/redirects.ts

    3. Seconds, not a rebuild. Measure it.

      Files src/rollback.ts

    done when · tick each as it passes
    watch 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.
  3. Running someone else's build safely

    A build runs arbitrary code without reaching anything it should not.

    1. A build runs arbitrary code from a repository.

      Files src/build/sandbox.tstests/build-isolation.test.ts

    2. A build that never finishes is a resource leak with a queue behind it.

      Files src/build/limits.ts

    3. Logs are read by people debugging and are the usual place secrets escape.

      Files src/build/logs.ts

    done when · tick each as it passes
    watch 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.
  4. A preview per change

    Every branch and pull request gets its own permanent address.

    1. Predictable per branch and per pull request.

      Files src/previews.ts

    2. Not per site. Someone will forget and it is found months later.

      Files src/serve.ts

    3. Without breaking permanent deploy addresses, which are the rollback path.

      Files src/cleanup.ts

    done when · tick each as it passes
    watch 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.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more hosting.