Sådan reducerede vi vores omkostninger til webinfrastruktur med 80 % ved at skifte fra Vercel til Cloudflare Workers + Railway

Vi flyttede vores webstack fra Vercel til Cloudflare Workers og Railway og erstattede Next.js med en kombination af TanStack Router, TanStack Start og Hono, alt efter hvad de enkelte opgaver krævede. Resultatet var cirka 80 % lavere infrastrukturomkostninger, og besparelsen steg til omkring 90 %, efter at vi begyndte at filtrere skadelig automatiseret trafik fra i edge-netværket. Det meste af migreringsarbejdet blev udført af AI-kodeagenter, mens ingeniørerne fokuserede på arkitektur, begrænsninger, gennemgang og verificering i produktion.

23. september 2026

Quang Lam · Founder & CEO

Sådan reducerede vi vores omkostninger til webinfrastruktur med 80 % ved at skifte fra Vercel til Cloudflare Workers + Railway

I årevis var Vercel det naturlige sted at udrulle næsten alt, hvad vi havde på nettet hos WebCatalog. De fleste af projekterne brugte Next.js, så kombinationen var oplagt. Vores marketingsites, produktgrænseflader, kontoindstillinger, udviklerkonsol, autentificering, administrationsværktøjer og API'er endte alle på stort set den samme teknologistak.

Det fungerede godt i lang tid. Vercel gjorde udrulninger enkle, Next.js gav os et produktivt fullstack-framework, og vi behøvede sjældent at tænke på infrastrukturen bag nogen af delene.

Med tiden passede den bekvemmelighed ikke længere til vores produkter.

Vores marketingsites havde stadig gavn af SSR (server-side rendering), men det havde de fleste af vores andre webapps ikke. De kunne være almindelige SPA'er (single-page applications). Vores API'er havde brug for et normalt Node.js-miljø med native afhængigheder og langvarige processer. Næsten alt andet i vores frontend-stak brugte allerede Vite. Samtidig blev infrastrukturomkostningerne sværere at ignorere, især efterhånden som den offentlige og automatiserede trafik voksede.

I første omgang forsøgte vi at optimere det, vi allerede havde. Vi overvejede at beholde Next.js og flytte det til Cloudflare Workers via en adapter. Vi prøvede Astro til marketingsitene. I en kort periode kørte vi vores API'er på Cloudflare Containers.

Til sidst landede vi på en enklere løsning: De fleste af vores webapps bruger nu Vite + TanStack Router på Cloudflare Workers, vores marketingsites bruger TanStack Start med SSR på Cloudflare Workers, og vores API'er bruger Hono + tRPC på Railway som langvarigt kørende Node.js-containere.

Migreringen reducerede vores omkostninger til webinfrastruktur med cirka 80 %. Da vi også strammede vores sikkerhedsregler i Cloudflare og blokerede mere fjendtlig automatiseret trafik i netværkets yderste led, steg den samlede besparelse til omkring 90 %.

Det meste af selve migreringen blev også udført af AI-kodeagenter, primært Claude Fable 5 og Claude Opus 5, mens ingeniørerne besluttede arkitekturen, fastlagde kravene, gennemgik ændringerne og verificerede resultatet.

Vi begyndte ikke med at forlade Vercel

Vores første indskydelse var at optimere den eksisterende opsætning.

webcatalog.io var det mest oplagte sted at begynde, fordi sitet får meget offentlig trafik, mens de fleste sider ændrer sig forholdsvis sjældent. Vi fandt ud af, at for meget af sitet stadig blev renderet dynamisk. Derfor rettede vi utilsigtet dynamisk rendering, tilføjede ISR (Incremental Static Regeneration) til ruter med meget trafik, gjorde flere sider cachebare og optimerede ressourcekrævende behandling af sitemaps.

Arbejdet hjalp, men gjorde også det underliggende problem tydeligere. Vi brugte stadig mere tid på at forstå, hvorfor forholdsvis statisk indhold var dynamisk, hvilken adfærd i frameworket der havde forårsaget det, og hvilken mekanisme i frameworket vi skulle bruge for at gøre indholdet cachebart igen.

Mange af vores andre applikationer havde imens det modsatte problem. Kontoindstillinger, udviklerkonsollen, administrationsapplikationer og de fleste produktgrænseflader havde slet ikke brug for serverrendering. De var browserapplikationer, der kommunikerede med API'er.

På det tidspunkt holdt spørgsmålet op med at være »Hvordan gør vi vores Vercel-regning mindre?« og blev i stedet »Hvad ville vi bygge, hvis disse applikationer ikke allerede var Next.js-applikationer?«

Vi overvejede Next.js på Cloudflare

Den mindst indgribende løsning var at beholde Next.js og flytte det fra Vercel til Cloudflare Workers.

Det overvejede vi seriøst, fordi Next.js kan køre på Cloudflare gennem adapterlag, så vi kunne have bevaret en stor del af den eksisterende applikationsstruktur.

Men vi betragtede ikke Next.js på Cloudflare som det samme som Next.js på Vercel. Next.js er tættest integreret med Vercels runtime, udrulningssystem, cachingadfærd og platformsfunktioner. På Cloudflare skal en adapter omsætte de forudsætninger til et andet runtime-miljø.

Det kan fungere fint, men når målet var at forenkle stakken, virkede endnu et kompatibilitetslag ikke ideelt.

Der var også et bredere spørgsmål om værktøjer. Næsten alt andet, vi byggede på frontend, brugte allerede Vite: desktopapplikationer, browserudvidelser, SPA'er og andre klientsideprojekter.

Next.js havde i høj grad bevæget sig mod Turbopack, og Turbopack er blevet markant bedre. Det var ikke en klage over ydeevnen. For os var den større forskel økosystemet. Vite var allerede det fælles fundament for det meste af vores frontend-kodebase, med et bredere pluginøkosystem og mere konfiguration, der kunne genbruges på tværs af projekter.

At beholde Next.js på Cloudflare ville derfor have løst en del af hostingproblemet, men det ville have bevaret både forskellen mellem frameworks og et separat økosystem af buildværktøjer.

Så vi trådte et skridt tilbage og så på, hvad hver type opgave faktisk havde brug for.

Vi opdelte stakken efter opgavetype

Da vi holdt op med at lede efter én universel erstatning for Next.js, blev arkitekturen meget enklere.

Vores marketingsites havde brug for SSR, fordi de har offentlige, lokaliserede sider, metadata, kanoniske URL'er, sitemaps og trafik fra søgemaskiner.

De fleste af vores andre webapps havde ikke brug for SSR og kunne ganske enkelt være Vite-applikationer med TanStack Router.

Vores API'er havde slet ikke brug for et React-framework og egnede sig bedre til et almindeligt Node.js-runtime-miljø.

Den endelige opdeling så sådan ud:

Webapps
Vite + TanStack Router
        │
        └── Cloudflare Workers

Marketingsites
TanStack Start + SSR
        │
        └── Cloudflare Workers

API'er
Hono + tRPC
        │
        └── Railway / Node.js-containere

Reglen blev ligetil: Brug SSR, hvor det giver reel værdi, brug en almindelig SPA, hvor det ikke gør, og kør backend-tjenester i et backend-runtime-miljø.

De fleste webapps blev til SPA'er

SPA-migreringerne var den letteste del.

WebCatalogs webapp blev flyttet særligt hurtigt: Vi oprettede grundstrukturen til erstatningen med Vite + TanStack Router, overførte brugergrænsefladen, skiftede udrulning til Cloudflare Workers og fjernede den gamle Next.js-version samme formiddag.

Vi gentog mønstret for Lexibirds webapp, kontoindstillinger, vores udviklerkonsol, autentificering og administrationsapplikationer. Fra den første SPA-grundstruktur til sletningen af den sidste Next.js-applikation gik der omkring 19 dage.

For disse applikationer er Cloudflare Workers bevidst uinteressant. Vite bygger statiske filer, Cloudflare leverer dem, og applikationsruter falder tilbage på index.html. Der er ikke noget SSR-runtime-miljø, for der er intet, der behøver serverrendering.

Det sværere var at bevare den adfærd, der med tiden var opstået omkring applikationerne. Noget autentificeringslogik måtte flyttes fra Next.js-serverruter til API'et, og ældre versioner af WebCatalogs desktopapp var stadig afhængige af gamle endpoints, som vi skulle holde i drift.

Det var let at flytte React-brugergrænsefladen.

Det var sværere at bevare de eksisterende grænseflader og aftaler.

Vi prøvede Astro og slettede det fem dage senere

Marketingsitene var sværere.

Vores førstevalg var Astro, som lignede et naturligt match til webcatalog.io og lexibird.com, fordi begge er offentlige sites med meget indhold, og Astro fungerer godt sammen med Cloudflare.

Vi begyndte at overføre begge sites, og fem dage senere stoppede vi.

Der var ikke én afgørende fejl. I stedet hobede små uoverensstemmelser sig op. Nogle ruter havde brug for databaseadgang under prerendering, mens vores buildmiljø bevidst ikke havde adgang til produktionsdatabasens legitimationsoplysninger. React islands gjorde det mindre naturligt at dele applikationstilstand. Vi stødte også på forskelle mellem prerenderede ruter og ruter, der blev renderet ved kørsel, samt nogle problemer med værktøjerne i vores repository.

Ingen af problemerne var umulige at løse. Det var netop derfor, det var risikabelt at fortsætte. Vi kunne nemt have brugt flere uger på at løse det ene problem efter det andet.

I stedet slettede vi implementeringen og begyndte forfra.

AI-agenter ændrede økonomien i den beslutning. En stor del af det mekaniske arbejde med at overføre koden havde ikke kostet flere ugers ingeniørarbejde, så de allerede afholdte omkostninger var mindre, og det var lettere at erkende, at vi foretrak en anden arkitektur.

TanStack Start passede bedre

Vi begyndte migreringen af marketingsitene forfra med den oprindelige Next.js-kode, denne gang med TanStack Start.

Det passede langt mere naturligt, fordi vi allerede brugte TanStack Router i SPA-applikationerne. Routing, loaders, søgeparametre og navigation byggede derfor på lignende koncepter. Koden forblev almindelig React og TypeScript, og buildsystemet var Vite.

Vi overførte lexibird.com til TanStack Start på én dag. Derefter fulgte webcatalog.io.

Fordelen var ikke, at TanStack Start på magisk vis løste alle problemer. Fordelen var, at det passede til den retning, resten af vores stak allerede bevægede sig i.

I et monorepo betyder det noget. Fælles buildværktøjer giver flere plugins og konfigurationer, der kan genbruges, færre særtilfælde ved fejlfinding, færre skift mellem forskellige tekniske kontekster for udviklerne og færre projektspecifikke konventioner, som kodeagenterne skal opdage.

Cloudflare var meget billigere for vores trafik

Arkitekturen var kun en del af forklaringen på, at vores regning faldt. Cloudflares priser var en væsentlig faktor, især når det gjaldt båndbredde.

Cloudflare Workers Paid tager i øjeblikket primært betaling for forespørgsler og CPU-forbrug og opkræver ikke særskilt betaling for dataoverførsel eller båndbredde for Workers. (developers.cloudflare.com) Det betyder meget for offentlige websites, fordi en forespørgsel kan bruge meget lidt CPU og stadig overføre HTML, JavaScript, billeder, skrifttyper og andre filer.

Vercels prismodel omfatter også forbrugskategorier og inkluderede mængder relateret til netværk, herunder Fast Data Transfer og Fast Origin Transfer. (vercel.com) For vores trafikprofil var Cloudflares økonomi markant bedre.

Besparelserne kom ikke kun af at skifte udbyder. Vi flyttede også mange applikationer til statiske Vite-builds, reducerede unødvendig SSR, gjorde caching mere eksplicit og flyttede API-opgaver til et runtime-miljø, der passede bedre til dem. Men Cloudflares priser, især på båndbredde, bidrog væsentligt til den reduktion på cirka 80 %, vi oplevede.

Det tal gælder vores specifikke opgaver og trafik. Det er ikke en påstand om, at alle applikationer, der flytter fra Vercel til Cloudflare, vil spare 80 %.

API'erne endte på Railway

API'erne var et særskilt problem.

Selvom de var Next.js-projekter, var de allerede stort set uafhængige af Next.js. WebCatalogs API var på omkring 34.000 linjer, men kun syv filer importerede next/server. tRPC brugte allerede sin Fetch-adapter, og Inngest understøttede Hono.

Vi erstattede Next.js som HTTP-ramme med Hono. Den del var overraskende lille.

Det sværere spørgsmål var, hvor API'erne skulle køre. Almindelige Workers var ikke et godt match, fordi API'erne bruger Node.js og native afhængigheder som sharp.

Vi prøvede først Cloudflare Containers, som hurtigt fik os væk fra Vercel. Men efter at have kørt den opsætning i produktion fandt vi ud af, at driftsmodellen krævede mere manuel kapacitetstilpasning, end vi ønskede på det tidspunkt.

Så vi flyttede API'erne igen.

I dag kører de på Railway som almindelige, langvarigt kørende Node.js-container-tjenester. CI (continuous integration) bygger Docker-images og sender dem til vores registry, og Railway håndterer horisontal automatisk skalering.

Ikke alt behøver at køre i netværkets yderste led.

At forlade Vercel betød, at vi selv fik mere ansvar

De lavere omkostninger havde en pris.

Vercel og Next.js havde håndteret mange infrastrukturfunktioner på et højere niveau. Med TanStack Start og Workers gik vi i stedet i retning af eksplicit HTTP-caching. En side renderes, vi returnerer cache-headere, og Cloudflare cacher svaret. Caching i browseren og i netværkets yderste led kan følge forskellige regler, herunder stale-while-revalidate i det yderste led.

Vi kunne godt lide, at de beslutninger blev eksplicitte, men eksplicit infrastruktur betyder også, at fejlene er ens egne.

Vi lavede et par stykker. En cacheregel i produktion kom ved en fejl til at matche besøgendespecifikke svar fra TanStack-serverfunktioner, og en anden cachekonfiguration fungerede dårligt sammen med SPA'ens fallbackadfærd. Fejlene var nyttige påmindelser om, at man ikke kan bevise, at en migrering er korrekt, alene ved at se på repositoryet. Produktionsinfrastrukturen er også en del af systemet.

AI-agenter ændrede måden, vi migrerede på

Det meste af implementeringsarbejdet blev udført af kodeagenter, primært Claude Fable 5 og Claude Opus 5.

Framework-migreringer egner sig usædvanligt godt til agenter, fordi meget af arbejdet har en tydelig eksisterende reference. Flyt denne rute, bevar denne URL, erstat dette framework-API, behold de samme metadata, kør typecheck, ret fejlene, og sammenlign resultatet med den gamle implementering.

Til webcatalog.io vedligeholdt vi en migreringsoversigt, der delte arbejdet op i faser som fundament, produktsider, katalogsider, søgning, blog, priser, redirects, sitemaps og overgangen til den nye løsning. I stedet for at bede en agent om at »migrere webcatalog.io til TanStack Start« gav vi den afgrænsede opgaver med eksplicitte krav til, hvad der skulle forblive uændret.

Den eksisterende applikation blev specifikationen. Agentens opgave var at genskabe adfærden med den nye arkitektur, ikke at genopfinde produktet.

Det flyttede den menneskelige indsats væk fra manuel oversættelse af kode og hen mod spørgsmål som: Har denne side faktisk brug for SSR? Hvilken adfærd er tilsigtet? Hvad må caches globalt? Hvilke URL'er skal forblive identiske? Hvor skal autentificering foregå? Hvornår skal vi holde op med at forsøge at få en tilgang til at fungere?

Vi gennemgik stadig resultatet, kørte builds og typechecks og verificerede manuelt områder med højere risiko som autentificering, SEO (søgemaskineoptimering), redirects og caching.

Den vigtige ændring var ikke, at AI skrev kode for os. Det var, at AI gjorde det billigere at eksperimentere.

Det blev meget lettere at retfærdiggøre at prøve Astro og slette det efter fem dage. Det gjorde mindre ondt at flytte API'erne én gang og derefter beslutte, at en anden platform passede bedre. Det mekaniske arbejde var billigere, så vi kunne skifte retning, når arkitekturen var forkert, i stedet for at fortsætte, fordi vi allerede havde investeret for meget i den.

Agenter gjorde implementering mindre til en knap ressource.

Det gjorde arkitektur, krav, gennemgang og verifikation vigtigere.

80 % blev til cirka 90 %

Efter migreringen var vores omkostninger til webinfrastruktur cirka 80 % lavere.

Derefter begyndte vi at se nærmere på, hvilke forespørgsler der overhovedet nåede frem til applikationerne.

En betydelig del af trafikken på det offentlige internet er automatiseret: søgemaskiner, AI-crawlere, agenter, scrapers, scannere og mindre venligtsindede bots. Noget af den trafik er nyttig, og noget er ikke.

Med applikationerne placeret direkte bag Cloudflare strammede vi vores sikkerhedsregler, så fjendtlig automatiseret trafik kunne afvises i netværkets yderste led, før den udløste beregninger i applikationerne eller arbejde i databasen.

Derefter nåede vores samlede besparelser op på cirka 90 % sammenlignet med den gamle opsætning.

Den endelige reduktion skyldtes flere ting i samspil: Cloudflares lavere priser, især på båndbredde; mindre arbejde på serversiden; et bedre runtime-miljø til vores API'er; mere eksplicit caching; og færre unødvendige forespørgsler, der overhovedet nåede frem til applikationerne.

Hvor vi endte

Der er ikke længere nogen Next.js-applikationer i vores repository. De fleste af vores webapps er nu Vite + TanStack Router-SPA'er på Cloudflare Workers. webcatalog.io og lexibird.com bruger TanStack Start på Cloudflare Workers, fordi de sites reelt har gavn af SSR. Vores API'er bruger Hono + tRPC på Railway og kører som almindelige Node.js-containere. Næsten alle vores frontendprojekter ligger nu i Vite-økosystemet.

Vores infrastrukturomkostninger faldt med cirka 80 %, i høj grad takket være Cloudflares markant bedre økonomi for vores trafik, især hvad angår båndbredde. Filtrering af fjendtlig automatiseret trafik i netværkets yderste led øgede den samlede besparelse til omkring 90 %.

Men det resultat, vi lægger mest vægt på, er arkitekturen. Marketingsites får SSR, alt andet, der kan være en SPA, er en SPA, og API'er kører på rigtige servere, når rigtige servere er den enklere løsning.

Vi begyndte med at forsøge at gøre Vercel billigere.

Vi endte med at indse, at vi ikke havde brug for det meste af den arkitektur, vi betalte for.