Gitrex Technologies
ReplitMigrationVercel

Moving a Replit app onto your own stack without breaking it

Replit Agent apps are more portable than people fear. The order of operations we use to move one to your own repo, Postgres and hosting: what to swap, what to keep, and how to cut over with a rollback.

· Founder, Gitrex Technologies

4 min read

The good news first. An app built by Replit Agent is usually an Express server, a React front end built with Vite, Drizzle for the database layer, and a Postgres database. That's a normal stack. Most of the migration is not rewriting code; it's untangling the handful of places where the app reaches for something only Replit provides.

Here's the sequence we follow. Order matters, because each step gives you a way to test the next one.

Step 0: get the code out before you touch anything

Replit can push to GitHub from the Git pane in the workspace. Do that first, into a repository you own, and confirm the push landed by cloning it somewhere else. Until this is done, the only copy of your product lives in someone else's account.

While you're there, look at the repo for a .env file that got committed. Replit stores secrets in its own Secrets panel, so this is rarer than with other tools, but it happens when someone "fixes" a missing variable locally.

Step 1: run it on your laptop

Clone the repo and try npm install && npm run dev. It will fail. The failures are the inventory of what's Replit-specific, and that's exactly what you want to see.

Typical list:

  • Environment variables that only existed in Replit Secrets. Grep for process.env. and write them all down.
  • process.env.REPL_ID, REPL_SLUG, REPLIT_DOMAINS and friends used to build URLs.
  • The database URL pointing at Replit's built-in Postgres (it's Neon under the hood).
  • A .replit file and replit.nix that define how the thing starts. Read them; they tell you the real start command and port.
  • Replit Auth, if the app uses it. It shows up as an OpenID Connect flow against replit.com with REPL_ID as the client id.

Nothing here is scary. It's just a list.

Step 2: the database

If the app uses Replit's Postgres, you have two options. Export and import into a database you own (Supabase, Neon on your own account, RDS, whatever), or, since Replit's Postgres is Neon, sometimes just create your own Neon project and restore into it so the connection semantics don't change.

Either way:

pg_dump "$OLD_DATABASE_URL" --no-owner --no-acl -Fc > snapshot.dump
pg_restore --no-owner --no-acl -d "$NEW_DATABASE_URL" snapshot.dump

Then run the Drizzle migrations against the new database to make sure the schema in code and the schema in the dump agree. If drizzle-kit push wants to change things, stop and understand why before continuing. That's usually a sign the schema was edited in the Replit database UI and never written back to code.

Do this on a copy first. You'll do it again, for real, at cut-over.

Step 3: swap the Replit-only pieces

Auth. Replit Auth is convenient inside Replit and nowhere else. Replace it with Supabase Auth, Clerk, or Auth.js depending on what the rest of your stack looks like. The migration path for users is the annoying part: you can't move password hashes you never had. In practice this means emailing existing users a "set your password" link on first login, and the code for that is a couple of hours.

Object storage. If the app uses Replit's object storage, it's an S3-compatible API. Point the client at your own bucket (S3, R2, Supabase Storage) and copy the objects across with rclone or the AWS CLI.

URLs and ports. Anywhere the code assembles a URL from REPLIT_DOMAINS or hard-codes the .replit.app domain, replace it with an APP_URL environment variable. Anywhere it listens on a port from .replit, make it read PORT.

Cron and background work. Replit Deployments can run scheduled jobs. On your own stack that becomes a Vercel cron, a GitHub Action, or a tiny worker on Railway. Write down every scheduled thing before you switch hosting, because these are the ones nobody remembers until a report doesn't arrive.

Step 4: hosting

An Express plus Vite app is a slightly awkward fit for Vercel, which prefers Next.js. Two reasonable paths:

Keep it as one Node process and host on Railway, Render or Fly. Least change, and the .replit start command translates almost directly into a Procfile or fly.toml.

Or split it: static front end on Vercel, API on Railway. More moving parts, better caching for the front end. I usually only recommend this when the front end is the bulk of the traffic.

Whichever you choose, set every environment variable from the Step 1 inventory in the new host before the first deploy, and use a separate database for previews. Never point a preview at production data.

Step 5: cut over with a way back

Deploy to the new host on a temporary domain. Test it against the copied database. Run the Stripe webhook tests if there are payments; Stripe will need the new webhook URL registered before cut-over, not after.

Then, in one sitting: take a final pg_dump, restore it to the new database, switch DNS to the new host, and register the new webhook endpoint. Keep the Replit deployment running for a week with its own database untouched. If something is wrong, flipping DNS back is the rollback, and you've lost nothing.

After a quiet week, pause the Replit deployment. Cancel it a month later.

What to keep

The code. Genuinely. Replit Agent's output is mostly readable TypeScript and Drizzle schemas are a fine way to define a database. The temptation to rewrite everything "properly" during a migration is how a two-week project becomes a two-month one. Migrate first, refactor later, when the app is on infrastructure you control and every change has a preview deploy.

That's the shape of every platform migration we do. The engineering is rarely the hard part. The hard part is being disciplined about doing it in this order.

Wajahat Shaw, founder of Gitrex Technologies

Wajahat Shaw

Founder of Gitrex Technologies. Senior engineer; builds and rescues software for founders, most of it React Native, Next.js and Supabase. Writes here about what actually breaks and how to check for it. More about Gitrex

Keep reading