flowingkhaosSign inBrowse the marketplace
New here? flowingkhaos is a build-in-public workshop. This is its weekly column.What this site is

Astro vs Next.js: Where My Back End Lives and How Fast It Actually Deploys

Astro vs Next.js: Where My Back End Lives and How Fast It Actually Deploys

Learn the real-world deployment times for Astro versus Next.js. This technical analysis shows that the CMS service took 90.8s to go live at the median.

Astro vs Next.js: The Answer, Split Architecture and All

Short Astro vs NextJS answer: Astro on Cloudflare Workers in front, small Bun-built services on Railway behind it, and every Railway service live in under two minutes at the median.

I’ll give you the long version. For my content site I picked Astro on Cloudflare Workers over Next.js, and the back end is a split: small Bun-built services on Railway, with Node running anything that carries Payload or a native addon. Every Railway service I measured went live in under two minutes at the median.

Now the caveat, before you get excited. The slowest single deploy of my api service took 490.4 seconds, and the Workers front end is measured on a different basis than the Railway services. Fast at the median is not fast every time.

Let’s get one thing straight: the claim is not “Bun is faster”. The claim is that my deploys stopped crawling, and I can show you the step timestamps. I measured this from Railway’s own step timestamps, every deploy each service still keeps, on 2026-10-21.

Here is the setup. flowingkhaos.com is Astro 7.3.2 with Preact islands, running on a Cloudflare Worker through @astrojs/cloudflare 14.3.1. Astro replaced my old Next.js app on 2026-09-09. The back end is a small Hono service with Better Auth on Railway, and my CMS, Payload, runs there too.

The framework that makes the whole thing work is the build-versus-run split. Bun installs and builds everything: in the Dockerfile, bun install and bun run build. Then Node 22 runs the production process for anything with Payload or a native addon in the tree. Bun runs in production only where nothing in the dependency tree needs Node. That is a rule I wrote down on 2026-08-30, and the rest of this piece is what it looks like in deploy times.

The Astro vs NextJS verdict in one line

So, Astro vs Next.js? For a content site with a separate back end, Astro on the edge and small services behind it. For an app full of per-request logic, read the concession section before you move anything.

The Deploy Table: Median, Build Time and the Slow Tail

Here is every deploy Railway still keeps for each of my four services, as I measured them:

Service Runtime Deploys Push-to-live p50 (min–max) Build p50
flowingkhaos api (Hono) Bun build, Node run, Dockerfile 98 (2026-10-08 → 10-20) 52.2 s (27.9–490.4) 40.9 s
khaosmedia.tv box (Hono) Bun run, Railpack 42 ok + 5 failed (2026-09-10 → 09-19) 69.8 s (42.6–243.2) 56.2 s
CMS (Payload) Bun build, Node run, Dockerfile 98 (2026-10-08 → 10-20) 90.8 s (23.4–414.2) 81.2 s
ian (sync server) Bun, Railpack 9 (2026-09-04 → 10-03) 104.5 s (65.0–418.5) 85.8 s

Push-to-live is the time from the deployment being created to the network being configured, meaning the new container is serving. Build is Railway’s BUILD_IMAGE step. Source: Railway deployment step events, measured 2026-10-21 (ours). The p50 is the median: half the deploys were faster than that number, half slower. The box row covers its 42 successful deploys.

Two things jump out of the table. The first is the gap between build and live. My api service builds in 40.9 s at the median and goes live in 52.2 s. That gap of about 11 seconds is the container being created and the network switched over. You don’t control it, and you shouldn’t forget it when someone quotes you a build time as a deploy time.

The second is the tail. Every service has one. The api maxed out at 490.4 s, the CMS at 414.2 s, ian at 418.5 s. I don’t know what each slow deploy was waiting on, and I won’t guess. What I know is that the median sits under two minutes for all four, and the worst case sits around seven or eight minutes.

Same shape, different weight: the Payload CMS and the api are both built by Bun and run by Node, and the CMS takes 90.8 s to the api’s 52.2 s.

The Front End Numbers, and What They Do Not Prove

The front end does not sit on Railway, so the table above does not cover it. My front-end numbers are thinner, and I’d rather show you exactly how thin.

On 2026-09-11, the first Workers Build of khaosmedia.tv, my other Astro site, went from the deploy hook to a deployed version in 38 s. That is one build. My API token cannot read Workers Builds logs, so I have no durations beyond that first one.

Locally, a production build of flowingkhaos.com with Bun 1.4.0 (astro build plus the Pagefind search index, 87 HTML pages) took 48.3 s and 64.2 s on 2026-10-21. Two runs. The index step took 2.8 s of that. The Worker itself holds 81 versions between 2026-09-12 and 2026-10-20, according to Cloudflare’s API, so the site does get deployed, often.

What this does not prove: that every service, front end included, goes live in under two minutes. I don’t have that number. The Railway table is measured on every deploy; the front end is one hook timing and two local builds.

Astro’s own docs describe Astro as the framework for building content-driven websites like blogs, marketing and e-commerce, and that matches why I picked it: my site is content first, with a few Preact islands for the parts that move. Astro’s 2023 Web Framework Performance Report draws on public field datasets rather than its own lab tests, and says no performance data was measured directly. Good for them for saying it. I measured my deploys.

Moving the site to Workers had side effects outside deploy speed too. One was SEO: I documented how Astro’s trailing slash on Cloudflare doubled my sitemap URLs and what the repair was.

Node 18 EOL, the Bun Lockfile and the 502 Behind a SUCCESS

Fast deploys came with a bill of small failures. Here they are, dated.

On 2026-09-10, Railway’s builder failed one of my Bun services with “Node.js 18.x has reached End-Of-Life”. A Bun service. Failing on a Node version I never asked for. The fix was declaring what I actually wanted, three ways: engines.node, packageManager and an .nvmrc. The builder guessed the runtime from what was missing, and it guessed wrong.

On 2026-09-11, Workers Builds shipped Bun 1.2.15, which cannot read a v2 bun.lock. My lockfile was v2. Three builds failed before I set BUN_VERSION=1.4.0 in the build settings. Same lesson as the day before: the platform’s default toolchain is not your toolchain until you pin it.

On 2026-09-12, Railway reported SUCCESS while the site answered 502. A 502 behind a SUCCESS. Is that a deploy? Technically, yes. Is it one I’d want on a Sunday? Absolutely not. Railway’s injected PORT overrode the one in my Dockerfile. And railway variable set does not redeploy, so changing the variable alone changes nothing until the next deploy.

The Bun tooling has its own small traps. bunx eslint fetches the wrong major version for my apps. bunx tsc wiped node_modules on 2026-09-05. Neither is a deploy failure, but both cost a build.

One honest note on the runtime rule. I didn’t benchmark Bun against Node myself. The rule rests on a study I read, which found real-world HTTP throughput between the runtimes converges within a few percent. That is corroborated, not measured by me, so I use Bun where it saves me time and Node where Payload needs it.

Why I Left Vercel: Queues, Caps and the Ignored Build Step

My Vercel story is not about build speed. I never measured Vercel build times, so I won’t compare them. It is about deploys that didn’t happen.

On 2026-08-24, a production deploy sat in QUEUED for 50 minutes. On 2026-08-25, the Hobby plan’s daily cap created no deployment row at all, so there was nothing to look at. Between 2026-08-25 and 08-26, an Ignored Build Step stopped deploys silently for about 22 hours.

Then the measurement. From Jul 31 to Aug 29, the Vercel dashboard showed my CMS at 22.2% and the old flowingkhaos Next.js app at 32.5% of the shared account’s active CPU: 54.7% together. The CMS moved to Railway on 2026-09-16, the site went to Workers, and the old Vercel project was deleted the same day. The full write-up is the fix Vercel Hobby could not carry the CMS, which documents the move step by step.

Now the concession. Next.js is the better tool for some jobs, and I’m not pretending otherwise. Kunal Ganglani’s 2026 comparison argues build times at scale favor Astro for static output, while noting that Next.js’s Turbopack has dramatically improved development server startup. Makers’ Den’s piece on why more teams pick Astro for content sites explains it the same way: Astro’s static-first, low-JavaScript approach suits blogs and docs. An app full of auth and per-request data is a different job.

Caching is the real trade-off. LogRocket’s Astro vs Next.js comparison notes that with ISR on Next.js, a cache miss costs a server response, typically 300 to 800 ms. That is a single source with no method given, so I hold it loosely.

What the Other Side Gets Right: Easton, PageSpeed Matters and the Source Conflicts

The outside evidence mostly agrees with my pick, which is exactly why I want to read it carefully.

Easton’s write-up, Astro vs Next.js: The Technical Truth Behind 40% Faster, argues the same direction from one author’s test. I agree that build speed on a static site is a real gap; it is still one test.

The strongest independent number comes from PageSpeed Matters. Their field data study matched Chrome field data to the framework on 490,000 sites (CrUX, May 2026): Astro passes Core Web Vitals on 48% of mobile sites, Next.js on 29%, Nuxt on 25%, against a web-wide mobile average of about 42%. That is real evidence. It also measures pass rates on other people’s sites, not my deploys, and its own headline says 29.3% while its body says 29%. Small, but it is the kind of thing I check.

Tech Insider’s Astro vs Next.js piece headlines “9KB vs 463KB JS”, while its body reports about 12 KB against about 187 KB on a docs site. Use the body figure, as a direction.

Lucky Media’s comparison for marketing sites says Astro sites typically score 90+ on all four Lighthouse categories without extra configuration. None of these sources measures the thing my split is about, which is where the back end runs and how fast it ships. That part only my own table covers.

Core Web Vitals pass rate by framework, mobile
Astro
48%
Next.js
29%
Nuxt
25%

Dashed line: web-wide mobile average, 42%

Source: Astro Passes Core Web Vitals on 48% of Sites. Next.js Manages 29.3% - PageSpeed Matters

The Money: $12 on Railway, $0 on Cloudflare, and the $25 Trigger

Here is the part with an invoice behind it. My September 2026 bills, in USD: Railway $12 for all my projects, Cloudflare $0. The back end, the CMS and the sync server all sit inside that $12. The front end on Workers costs nothing at my traffic.

I’m not going to compare that against a Vercel bill, because my record doesn’t hold one for the same workload. What I can say is that the move was driven by the 54.7% CPU share and the stuck deploys, not by a price list, and that the result is a monthly number small enough to read on one line. The whole stack, every paid tool, is detailed in my monthly stack costs column.

A cheap bill can still drift, so I wrote a trigger next to it: Railway usage over $25 a month for two months in a row, and I re-measure both hosts before deciding anything. Not a feeling. A number, written before I needed it.

The Split, Owned: CMS on Railway and the Standing Rule

The rule I follow behind all of this is short. Since 2026-08-30: Node in production wherever Payload or a native addon is in the tree; Bun for dev, build and CI; Bun as a runtime only for a dependency-light service with no Payload and no native addons.

My api shows the shape. It is Hono plus Better Auth, and its Dockerfile starts from node:22-alpine, runs bun install and bun run build, then starts node dist/index.js. The CMS is the same: Bun builds it, Node runs it. The khaosmedia.tv box is the exception the rule allows: a small Hono service with nothing native, so it runs on Bun directly. Moving the CMS onto Railway while the data stayed where it was is its own story, told in own your content: a control test, not copyright.

I’ll be the first to admit I didn’t notice the waste for a while. While measuring, I found that api and the CMS each deployed about 100 times in 12 days, because a push to my monorepo redeploys them even when the commit touched neither. The latest deploy of both came from a commit that changed only OSI. Deploys were fast enough that I never looked.

What I did not measure: no Bun-vs-Node benchmark of my own, no uptime figures, no Workers build durations past the first, and no claim about anyone else’s stack. More of what broke along the way lives in the fixes. If you’re weighing Astro vs Next.js for a content site, measure your own deploys from the platform’s step timestamps first. Then decide.

Sources

Questions, answered

What architectural setup was used for the content site?
The content site used Astro on Cloudflare Workers, with a split back end consisting of small Bun-built services running on Railway.
How fast were the deployments for the API service?
The median push-to-live time for the flowingkhaos `api` service (Hono) was 52.2 seconds, with a max time of 490.4 seconds.

Keep reading

The column · weekly · free

The next piece, the morning it is published.

One written column a week. The email service holds the list.

The columns, fixes and workshops continue in the community: SEO, AI, and making money with code.

Join on Whop

Luke Sidney writes and builds everything here, alone, in the open. lukesidney.me