Skip to content
Engineering

How one click deploy ships to the edge: cache patterns, R2, and atomic rollouts

BYOB Team

BYOB Team

11 min read

One click deploy builds SvelteKit, hashes assets for a year of edge cache, uploads through Cloudflare Pages and Workers, and cuts over atomically across 348 cities so visitors see a consistent version. R2 stays behind signed routes, HTML revalidates quickly, APIs stay fresh, and rollbacks restore a snapshot when needed.

Key takeaways

  • • Build creates content hashed assets so edge nodes cache for a year and new deploys get fresh URLs automatically
  • • Pages serves static files, Workers run server logic, R2 stays behind signed server routes, HTML revalidates quickly, APIs stay uncached
  • • Atomic cutover stages the new version then flips everywhere at once so visitors never see a mixed site
  • • Rollback restores a snapshot and redeploys, preview URLs carry risk before production, and the deploy checklist gates every release
How one click deploy ships to the edge: cache patterns, R2, and atomic rollouts

An airport where the whole fleet lifts at once #

Picture an airport where check in, baggage, and boarding roll on one conveyor. Bags get tagged with a code that never repeats, carts move only when the gate lights turn green, and the fleet lifts on a single signal. That is how a clean edge deploy feels. Build tags every file with a hash, the conveyor carries the bundles to hundreds of gates, edge nodes cache and revalidate by rule, storage stays behind a guarded dock, and the cutover flips everywhere at once.

airport style conveyor moving hashed bundles to edge gates across a map, one build flipping live in a single atomic motion
airport style conveyor moving hashed bundles to edge gates across a map, one build flipping live in a single atomic motion
*One build, many gates. Hashed bundles travel to edge nodes and flip live in a single atomic motion.*

How does one click deploy reach the edge? #

BYOB compiles a SvelteKit project and ships the output to Cloudflare Pages with server logic running as Workers. That path is not a guess. The repo config says it plainly in byob_front/svelte.config.js.

js
import adapter from '@sveltejs/adapter-cloudflare';

export default {
  kit: {
    adapter: adapter({
      routes: {
        include: ['/dash/*', '/api/*'],
        exclude: ['<files>', '/blog/*', '/tools/*', '/prompts/*', '/quiz', '/tos', '/privacy', '/auth', '/contact', '/showcase']
      }
    })
  }
};

The adapter decides what stays static and what must run per request. Marketing, blog, tool, and quiz routes prerender to files that edge nodes can cache, and the same holds for the other excluded paths. Dash and api routes stay dynamic and execute in isolates near the visitor. The shape is documented in the adapter cloudflare docs, which explain the Workers Static Assets output and the .svelte-kit/cloudflare target that later deploys through the Pages and Workers APIs.

Behind the button, the pipeline tracks every publish in the database. Commit f844f70 introduced the deploykmenttable that later settled as public.deployments with states deploying, deployed, and failed, linked to projects and commits, plus a deployment_url and timestamps for deployed_at and created_at. That table is the source of truth for live status, history, and rollback. A failed validation never leaves deploying, the live version keeps serving, and the UI can show line numbered logs instead of a blank screen. The user facing side of the same pipeline is documented in one click deployment explained, which describes one click to a live URL in about 30 seconds with a free subdomain under byob.page plus auto SSL and custom domains on paid plans.

The network it ships to is large and already proven. Cloudflare lists 348 cities on its network page, and notes that 95 percent of the connected population sits within 50 milliseconds of a data center. The Pages docs describe full stack apps deployed to that network with rollback and server functions built in. In practice that means a visitor in Mumbai hits a nearby point of presence, not a single origin far away, and time to first byte often lands under 50 milliseconds when payloads are lean and caching is correct.

Try the release gate we use internally: deploy checklist. It returns 200 and we keep it as a required check before any publish announcement.

Try it right here: deploy checklistOpen full tool

Loading the interactive tool… or open it here.

Across hosts the pattern repeats. Vercel and Netlify both describe build then deploy then publish with hashed assets and atomic cutover, as described in Vercel docs and Netlify docs. Replit wraps the same idea with preview URLs before production.

What cache rule fits each layer? #

Caching is where speed is earned or lost. The rule is simple to state and easy to violate by accident. If the URL contains a hash, cache long. If the URL is HTML that points at hashed assets, cache briefly. If the response is data that changes per visitor, do not cache at all.

layer where it runs cache rule failure mode fix
static assets Cloudflare Pages edge cache content hash, cache 1 year, immutable stale bundle after deploy if filename reused never reuse filenames, hash every build
html pages edge cache with short lifetime cache briefly then revalidate visitors stuck on old markup pointing at old hashes keep html lifetime short, use etag or revalidate
api routes Workers isolated runtime no cache, execute fresh cached auth or form response leaks private data set no store, validate session per request
r2 objects STORAGE binding behind server route signed route cache per policy, bucket itself not public public bucket exposes files or lets direct upload proxy all access through server routes with checks
ssl and dns edge plus origin auto provision and renew custom domain sits pending verify dns records then let issuance retry, do not reissue manually in a loop

Static assets carry a hash like app-a3f8d2.js. When content changes, the name changes, so no cache ever serves the wrong bytes under a new name. Old files stay in cache until evicted and harm nothing. HTML acts as the manifest that points at those hashed files, so HTML must turn over quickly. This is why the table pairs a year for assets with a brief window for HTML. The manifest must be fresh, the cargo can be permanent.

diagram of cache layers from hashed assets to html to api to r2, assets as cargo, HTML as manifest, APIs live, R2 as guarded warehouse
diagram of cache layers from hashed assets to html to api to r2, assets as cargo, HTML as manifest, APIs live, R2 as guarded warehouse
*Think in layers. Assets are cargo, HTML is the manifest, APIs are live, R2 is the guarded warehouse.*
flowchart LR A[Build and hash] --> B[Upload hashed bundles] B --> C[Propagate to edge via Pages] C --> D[Run api via Workers] D --> E[Serve cached or fresh] E --> F[SSL active and dns live] F --> G[Atomic cutover everywhere] B --> H[R2 objects via STORAGE binding] H --> E

How does R2 keep uploads safe at the edge? #

Object storage at the edge is only as good as its front door. BYOB attaches a Cloudflare R2 bucket as the STORAGE binding and routes every file operation through SvelteKit server routes. The browser never sees bucket names, and the bucket never sees the browser. Server code reads the binding as a runtime variable and streams bytes only after checks pass. The storage side is documented in BYOB storage and R2, which covers the R2 bucket behind server routes so the browser never touches bucket policy directly.

That guard matters because file uploads are easy to get wrong. Client side validation can be bypassed, filenames can carry paths, and MIME types can lie. The secure shape proxies through a route that verifies the session, validates type and size on the server, writes under a generated key such as avatars/{userId}/{uuid}.webp, and returns only the key. Private sharing then uses application signed URLs that point back at the app domain, not at a raw bucket endpoint. The route validates the token and streams from STORAGE, which lets you add expiry, counting, or per request auth without touching bucket policy.

The commits show how this came together. r2-integration landed the initial bucket service and the StorageBrowser.svelte component under src/lib/components that lets a project browse and manage files from the dashboard. r2fix tightened the integration after early testing, and the history from 5a64230 through df347a0 and 9e2a6e3 traces the storage docs, the R2 service helpers, and the proxy routes settling into place. The result is visible in the dashboard today. Files appear in a browser component, but writes still flow through server routes, so the UI is convenience and the route remains the gate.

The R2 docs describe zero egress fees, so many young apps see storage cost near zero and pay mainly for request volume. Across builders the warehouse idea stays stable. Pages plus R2, Vercel blob, Netlify blob, and Replit runtime all keep files behind server routes with validation and signed private links.

How do rolling releases stay atomic across providers? #

Atomic means staged then flipped, not streamed. The new version builds and uploads while the old version still serves. Only when the new bundles have propagated does traffic cut over everywhere at once. Visitors see old or new, never a hybrid where the header is new and the checkout is old.

The mechanics vary by host but the pattern repeats.

On Cloudflare Pages, atomic deploys stage full assets internally and flip traffic after replication, with instant rollback to a prior deploy. Vercel lands immutable deploys with content hashed assets and then promotes one deploy to production, which is also atomic by design, as described in Vercel docs. Netlify publishes atomically and keeps prior publishes ready for a fast restore, as described in Netlify docs. BYOB models this with a deployments row that tracks url, slug, and state, and with version snapshots that let you restore a known good build and redeploy in about a minute.

Failure handling follows the same conveyor logic. If build or validation fails, distribution never starts. If distribution succeeds and users hit errors, the snapshot path lets you undo with data rather than panic.

What are the trade-offs? #

Hashed builds plus edge cache plus atomic cutover win when visitors must never see a mixed site. Content hashed assets cache for a year, HTML revalidates quickly, APIs stay uncached, R2 sits behind signed server routes on the STORAGE binding, and Pages plus Workers flip everywhere at once across 348 cities.

Pick this pattern when Pick a simpler deploy when
Many edge nodes must serve one consistent version The site is a single static page with no server logic
Uploads need session checks before touching storage Files are public and need no access control
Rollback must restore a snapshot fast Deploys are rare and downtime windows are acceptable

It loses on preview risk and checklist discipline. Preview URLs can leak unfinished work, and every release still wants the deploy gate. Pick the alternative when atomicity buys nothing: direct static hosting with no server routes.

Who this is for (and who should skip it) #

This guide helps if you run a marketing site, portfolio, blog, docs, or app where most pages can prerender and the dynamic surface is forms, auth, and APIs that run as edge functions. You want to know how hashed assets cache, why HTML must revalidate, why R2 sits behind server routes, and how atomic cutover avoids mixed versions.

Skip it or extend it if your app needs local filesystem writes inside the runtime, long running batch jobs, video encoding, or persistent daemon processes. Those workloads belong in an external service, a queue consumer, or a dedicated compute layer beside the edge web tier. Build the web layer on the edge, route heavy work elsewhere, and keep deploys simple where they shine.

  • Best for developers tuning cache rules for prerendered pages plus edge APIs.
  • Best for startups keeping uploads safe behind server routes and R2.
  • Best for small teams wanting atomic rollouts without mixed versions.

What we learned building this #

We learned the value of making the adapter explicit. byob_front/svelte.config.js now declares the Cloudflare target and the route split between static and dynamic, so a new route cannot drift into the wrong tier by accident. That one file explains why /blog prerenders and /dash stays live.

We learned to track deploys like any other domain object. Commit f844f70 created the deployments table, and later merges under deplTable shaped its indexes and policies. That history made failure handling calm rather than heroic. A build that fails keeps the last deployed row live, and a dashboard viewer can reason from dates and urls instead of hunting logs.

We learned to guard R2 at the route rather than at the bucket. The sequence from 5a64230 through df347a0 and 9e2a6e3 wired the STORAGE binding, fixed early edge cases as r2fix, and surfaced browsing through src/lib/components/StorageBrowser.svelte. The component is helpful for operators, but the server route remains the gate for uploads, downloads, and signed sharing. Proxy with checks keeps the domain consistent and the credentials where they belong.

What is on the rollout checklist you can copy? #

Run this before you press publish, and again after traffic settles.

  1. Confirm preview url matches expectations on mobile and desktop, including auth callbacks and form submissions
  2. Verify dns records for custom domains and wait for propagation signals before assuming ssl is slow
  3. Check that hashed assets build with new filenames and that old urls still serve for active sessions
  4. Confirm api routes set no store and that sensitive endpoints check session before reading storage
  5. Test R2 flows through server routes with an allowlist for type and a cap for size, then test a signed private link
  6. Review adapter settings in svelte.config.js so new routes land in the intended tier
  7. Keep the last working deployment snapshot labeled and rehearse restore plus redeploy once
  8. After publish, watch error rate before traffic, then latency percentiles, then regional slices

That is the conveyor in checklist form. Build, hash, upload, edge, cache, live. When each station does its one job well, one click is enough.

Sources and further reading #

All links below were verified to return 200 on publish day via direct fetch. Cloudflare lists 348 cities on its network page. Pages specifics and atomic behavior come from the Pages docs. R2 binding behavior and pricing come from the R2 docs. SvelteKit Cloudflare output is documented in the adapter docs. Comparable patterns for hashed assets and function hosting appear in Vercel docs and Netlify docs. The internal tool link is the deploy checklist.


Ship small and watch the conveyor. Run the deploy checklist →

How we picked these

Sources were fetched with curl to verify 200, including the BYOB deploy and storage posts. Cloudflare network cites 348 cities with 95 percent of the connected population within 50 milliseconds, Pages docs describe edge distribution, R2 docs describe bindings, SvelteKit adapter docs describe Cloudflare output, Vercel docs describe comparable build to edge flows. Commits and config were read from the repo rather than inferred.

Frequently asked questions

How long do hashed assets stay cached at the edge?

Hashed assets can cache for a year because the filename changes when content changes. HTML caches briefly so updates appear quickly, and API routes stay uncached so data stays fresh.

Why does R2 sit behind server routes instead of direct bucket URLs?

Server routes check the session and validate type and size before touching the STORAGE binding. That keeps credentials in the worker environment and keeps shared links on your own domain.

What makes a deploy atomic?

The new version stages internally, then traffic flips everywhere at once. Visitors see either the old version or the new version, never a blend of both.

How does this differ on Vercel or Netlify?

The building blocks match. Content hashed assets cache long, HTML revalidates, functions run near the visitor, and releases cut over atomically. Platform names differ, the pattern stays constant.

When should you not rely purely on the edge?

Skip pure edge if you need filesystem access, long running jobs, or large file processing that exceeds worker limits. Put those workloads in a backing service and keep the web layer on the edge.

About the Author

BYOB Team

BYOB Team

The creative minds behind BYOB. We're a diverse team of engineers, designers, and AI specialists dedicated to making web development accessible to everyone.

Ready to start building?

Join thousands of developers using BYOB to ship faster with AI-powered development.

Get Started Free