← All case studies

Case study

GCH Cloudflare Migration

Moving Greater Change Health's application portfolio off a single shared VPS onto Cloudflare's edge network, and off Supabase onto Neon Postgres, one app at a time.

Delivered with our partnerHenge
GCH Cloudflare Migration logo
GCH Cloudflare Migration redesigned website

Project overview

One VPS, one migration pattern, repeated across a portfolio.

Greater Change Health's billing platform and Nora were two of roughly eight client applications we'd originally shipped on a single shared VPS. As that portfolio grew, we migrated it, and its neighbors, onto Cloudflare's edge network, and off Supabase onto Neon Postgres, treating every application as its own sequenced migration project.

The challenge

A shared VPS is one incident away from taking every client down.

Roughly eight client applications, including Greater Change Health's billing platform and Nora, plus several other internal dashboards, were all running off one Ubuntu VPS. Supabase's free tier caps a project at two active databases per account, so a growing portfolio meant juggling multiple Supabase accounts just to keep each app on a free plan.

What we did

Migrate app by app, verify every step.

We treated every application as its own migration project: ported each Next.js app from 14 to 15 (React 18 to 19) through OpenNext so it could run as a Cloudflare Worker, and moved each Postgres database onto Neon, whose free tier allows up to 100 projects per account instead of Supabase's two. Data moved through idempotent, read-only scripts that pulled every row from Supabase's REST API and verified row counts before cutover, and each domain went DNS-only and passed a login and data smoke test before switching to Cloudflare's proxy, with the original VPS kept live as a rollback the entire time.

What the migration actually involved

Real edge-runtime problems, not just a hosting swap.

01No native Node addons

Workers can't run native bcrypt, so password hashing moved to bcryptjs or Web Crypto, depending on the app.

02Stateless by default

Apps that stored data in local JSON files or relied on a persistent Postgres pool needed real datastore changes: Cloudflare D1 for Nora, Neon's serverless driver for the rest.

03Subtle data-shape differences

Neon's driver returns native JS dates where Supabase's REST API returned strings, and legacy Supabase auth cookies were oversized enough to break Nginx. Both had to be found and fixed, not assumed away.

04A sequenced DNS cutover

Mail records moved first, then each app switched from DNS-only to proxied only after passing its own smoke test, with the original VPS kept live as a fallback.

The Results

A portfolio consolidated onto one edge platform.

The result was a client portfolio's worth of Postgres consolidated onto a single Neon account instead of several Supabase accounts split to stay under free-tier limits, and roughly eight applications moved off one shared VPS onto Cloudflare's edge network, most needing their own identity layer rebuilt along the way, since Neon doesn't bundle an auth service the way Supabase does. Every app kept its own rollback path until its cutover was verified.

Discuss your own project