Blog Mar 21, 2026 4 min read

How coding agents can run a real app cutover from Supabase to a Laravel monolith

A real app cutover is not code generation. It is source freezing, production import, ingress control, DNS timing, provider state capture, and enough narrative discipline to turn the work into repeatable operator leverage. This is how the RankWar migration into the lmachine Laravel monolith was actually run.

Most “AI deployment” content is about typing faster

That is the wrong benchmark.

The real benchmark is whether a coding agent can carry an app across the whole cutover:

  • read the legacy system
  • freeze the source data
  • import it into the destination
  • control the production plane
  • verify the live runtime
  • document the move so the next operator does not start from folklore

If the agent can only scaffold code, it is still a junior typist with better autocomplete.

The real power problem

RankWar started as a real but lightweight product on:

  • Next.js
  • Supabase Auth
  • Supabase Postgres
  • Resend
  • Vercel-style domain and deployment assumptions

The destination was not “another app.”

It was a different operating system:

  • one Laravel 13 monolith
  • one shared Postgres database
  • one shared identity spine
  • many domains
  • one production box behind Dokploy, Traefik, and Tailscale

The job was not framework migration.

The job was control-plane migration.

Step 1: freeze the source system into a replayable artifact

Most people keep the old database live and wire the new runtime straight into it.

That is weak because the legacy system stays in charge.

The better move is:

  1. export a normalized private snapshot from the live source
  2. treat that snapshot as the migration contract
  3. keep replaying against the destination until the import is boring

For RankWar, the agent exported the live Supabase project into a private immutable snapshot and proved the import locally before touching live production.

That changed the migration from guesswork into an actual contract.

Step 2: separate shared spine from bounded context

When multiple apps will live in the same backend and database, the schema split matters more than the framework swap.

Weak move:

  • keep legacy generic names forever
  • or over-generalize everything into one fake shared model

Winning move:

  • shared primitives stay shared
  • product tables stay explicit

Inside the monolith that means shared tables like:

  • users
  • social_accounts
  • apps
  • app_domains
  • contacts
  • user_app_access
  • outbound_emails

And RankWar-specific tables like:

  • rankwar_campaigns
  • rankwar_entries
  • rankwar_referrals
  • rankwar_events

That is how one monolith can host many domains without turning into generic sludge.

Step 3: import into production before you move public traffic

The weak sequence is:

  • point DNS
  • hope the app is ready
  • debug in public

The right sequence is:

  1. production import first
  2. ingress attachment second
  3. public DNS cutover third
  4. public TLS verification fourth

That way the monolith is already telling the truth before the internet does.

For RankWar, production now already contains:

  • 7 campaigns
  • 38 entries
  • 1 referral

So the remaining gap is not application readiness.

It is public DNS timing.

Step 4: treat provider state like infrastructure, not trivia

The cutover touched:

  • Supabase
  • Dokploy
  • Tailscale
  • Resend
  • Namecheap
  • Google Analytics
  • Search Console

Most teams let that state live in:

  • browser history
  • screenshots
  • chat transcripts

That is weak because the next operator inherits a scavenger hunt.

The stronger pattern is:

  • tracked docs for public truth
  • gitignored ops/private/ files for credentials, provider IDs, and live control-plane notes
  • immutable source snapshots for migrations

That turns provider state into operating memory instead of superstition.

Step 5: make the agent own the boring but high-leverage moves

The interesting part of this migration was not writing Blade templates.

It was the chain:

  • export the live Supabase source
  • import into Laravel
  • redeploy app roles in Dokploy
  • attach domains
  • validate the live Docker services through SSH
  • verify production routes and host behavior

That is the work that usually gets trapped in “someone from the team knows how.”

That is exactly the work the agent should absorb.

What most people will do instead

They will do one of three loser moves:

  • keep Supabase permanently in the loop because it feels safer
  • split RankWar into a separate backend forever because it feels cleaner
  • celebrate the migration before production data and ingress actually converge

All three fail for the same reason:

they preserve old boundaries instead of upgrading the operating system.

The real lesson

Coding agents get interesting when they stop being code generators and start becoming control-plane operators.

That means they can:

  • move across browser-authenticated provider surfaces
  • move across SSH and Docker surfaces
  • manipulate deployment state
  • preserve secrets discipline
  • turn the whole run into public narrative and private memory

That is the bar.

Anything below that is just typing faster.