The one-line version: on the leading vibe-coding platforms you can already put your app in a GitHub repo you own — but "synced to GitHub" is not "migrated off the platform." The code moves cleanly; your secrets and often your database do not. Do the move in the right order, while the app is healthy, and you never take downtime. Skip a step and you learn — usually in production — that a repo which builds on your laptop still can't serve a request.

If your app is on Lovable, it's likely already mirrored: Lovable's native GitHub integration is a two-way sync on your default branch, so every prompt you send becomes a commit in your repository, and every push you make to GitHub flows back in. On paid plans you export the full project and own the complete source, deployable to Vercel, Netlify, AWS, or your own server. Replit, Bolt, and Emergent offer their own paths to GitHub. The runbook below is platform-agnostic; it's the order that matters.

1. Connect GitHub and confirm the repo is the source of truth#

If you haven't already, connect the platform's GitHub integration and let it create (or push to) a repository under your account. Then verify it's real: clone the repo to your machine, not the platform's editor.

git clone https://github.com/you/your-app.git
cd your-app

If you get the whole tree with a commit history, you have a live mirror. If the platform only offers a download-only ZIP, that's a dead snapshot — note it, because it changes step 6 (you'll have no ongoing fallback).

2. Get a clean local build#

Install and run it locally against nothing but the repo. It will almost certainly fail — that's the point of doing this now.

npm install
npm run build   # or the framework's dev command

Whatever it complains about — a missing env var, a hardcoded platform URL, an SDK that assumes the platform runtime — is a migration task. Write each one down.

3. Inventory and re-create your secrets — this is the step everyone skips#

Environment variables and API keys do not transfer. They live in the platform's secret store, and your .gitignore correctly keeps .env out of Git, so the export cannot include them. This is the number-one cause of "it built, why is it 500ing."

Grep the code for every variable it reads at runtime:

grep -rhoE 'process\.env\.[A-Z0-9_]+' src | sort -u

That's your checklist. Re-create each one in your new host's environment (Vercel/Netlify project settings, a secrets manager, or a local .env you never commit):

# .env  — never commit this; it stays in .gitignore
DATABASE_URL=postgres://...
STRIPE_SECRET_KEY=sk_live_...
OPENAI_API_KEY=sk-...

Rotate any key that was ever visible in the platform's client bundle while you're here.

4. Provision your own database before you touch hosting#

If your app uses the platform's bundled database, auth, or storage, the code comes with you but the data and the managed service may not. Stand up your own — a managed Postgres is the usual landing spot — run your migrations against it, and move the data:

pg_dump "$OLD_PLATFORM_DB_URL" > dump.sql
psql "$NEW_DATABASE_URL" < dump.sql

Point the app's connection string at the new database and confirm it reads and writes correctly. Do this before you flip hosting, so the new deploy comes up on a backend you already trust.

5. Wire a minimal CI#

The vibe-coding loop was quietly catching some regressions for you. Once you leave, nothing does — unless you add it. A three-check pipeline is enough to start:

# .github/workflows/ci.yml
name: ci
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npm run lint --if-present
      - run: npm run typecheck --if-present
      - run: npm test --if-present

Lint, typecheck, and whatever tests exist. It's not comprehensive; it's a floor, and a floor is what an acquirer's engineer will look for.

6. Deploy to your own host — in parallel, not as a cutover#

Deploy the app to your target host (Vercel, Netlify, Fly, a container, whatever) using the secrets from step 3 and the database from step 4. Keep the platform's deployment running the whole time. Verify the new deployment against a preview URL: real login, real write, real payment in test mode. Only when the parallel deploy is green do you move traffic.

7. Move DNS last#

Point your domain at the new host. Because you verified in step 6, this is a routing change, not a leap of faith. Watch error rates for a few hours.

8. Decommission the platform — deliberately, and last#

Now, and only now, decide what to do with the platform. Two sane options: leave the two-way sync on and treat the platform as one more contributor to your repo, or make the repo the sole source of truth and stop pushing from the platform. What you must not do is keep two "sources of truth" indefinitely — that's how a platform edit silently clobbers a hand-fix. Cancel the plan once the new stack has served real traffic and the mirror is provably redundant.

The two forcing functions#

Do this while the app is boring and healthy. The two events that turn should into must are an outage — where you suddenly need to debug infrastructure you don't own — and a security questionnaire from your first enterprise buyer, who wants to see your repo, your CI, and how you handle secrets. Both are the worst possible time to discover that your secrets never transferred and your database won't leave. A vibe-coded app that reaches real users has already earned the two hours this takes. Spend them on a normal Tuesday.

Related: the ownership check to run before you build, the maintenance and security checklist for a vibe-coded app, vibe coding vs spec-driven development, and why the customers paying for this can't code.