Scaling a Lovable app past the Supabase free tier
The default Lovable stack (Lovable plus Supabase) is genuinely good for the first version of an app. Single signup, generous free tier, auth and database and storage bundled. The problems show up later, when your app has real users and the bill or the architecture starts pushing back.
This post is about that "later." What actually breaks, and what to do about it without rewriting your app.
Contents
- Where the default stack breaks
- Three paths off
- Migrating the database
- Keeping auth working during the move
- What you do not need to migrate
Where the default stack breaks
The free tier itself is rarely the problem. Most apps that hit the free-tier ceiling are happy to pay. The breaks are usually structural.
The pricing cliff. Supabase free is two projects, 500MB database, 5GB bandwidth. The Pro plan is $25 per month and covers most growing apps. But certain dimensions (database compute add-ons, larger storage, point-in-time recovery, longer log retention) are priced per dimension on top of the base. The bill can get to $150 per month surprisingly fast for an app with modest traffic but a database that grew.
Single engine. Supabase is Postgres-only. If your app needs anything else (Redis for sessions, vector search at scale, ClickHouse for analytics, MongoDB for a third-party integration) you are now multi-vendor. That is fine, it just changes the operational story.
Auth coupled to the database. Supabase auth lives in your Supabase Postgres. The auth.users table is in the same instance as your application tables, and row-level security policies often reference it. Moving the database without moving the auth is harder than it looks because the foreign keys and RLS policies expect both to be present.
Connection limits. Supabase Pro caps pooled connections around 200 per project. A growing app with serverless functions can hit this in a single bad afternoon. The fix exists (more compute, transaction-mode pooling) but it is a fix you have to think about, and Lovable does not warn you about it in advance.
None of these are dealbreakers on their own. The decision to move usually comes when two of them happen at once.
Three paths off
There are three reasonable moves.
Stay on Supabase, pay for the upgrades. If you like the bundled developer experience and your app fits within Supabase's shape, this is the cheapest path measured in your time. Pay the $25 base, add the dimensions you need, do not migrate. The cost is a yearly $300 to $1800 depending on growth, but you keep your weekends.
Move the database, keep Supabase for auth. This is what most teams actually do. The Postgres goes to a database vendor (Layerbase, Neon, your own host). Supabase becomes auth-only. The free tier for auth alone is generous, your bill drops, and you keep the auth UX you already wired up. This is the lowest-risk move because it changes one thing at a time.
Move everything off Supabase. Database to a managed Postgres, auth to Clerk or Better-Auth, storage to R2 or S3. Most aggressive, biggest decoupling win, also the most work. Worth doing if you are committed to a multi-engine future or your team is large enough that the operational cost of three providers is less than the lock-in cost of one.
I will cover the middle path in detail because it is what most Lovable teams end up doing and the steps are concrete.
Migrating the database
The migration is the same shape as any Postgres-to-Postgres move: dump from Supabase, restore to the new host, repoint the connection string.
Step 1: Provision the new database
Visit layerbase.com/create/postgresql and create a Postgres named for the project (lovable-app-prod). Provisioning is about ten seconds.
Copy the connection string. It looks like:
postgresql://layerbase:<password>@your-host.cloud.layerbase.dev:5432/app?sslmode=requireStep 2: Dump from Supabase
Supabase exposes a direct connection string in your project dashboard (Settings, Database, Connection string, "URI"). Use it with pg_dump:
pg_dump \
--no-owner --no-acl \
--schema=public \
--schema=storage \
"postgresql://postgres:<password>@db.<project>.supabase.co:5432/postgres" \
> supabase-dump.sqlA few things:
--no-owner --no-aclstrips the Supabase-specific ownership and grants. Without these flags, the restore will try toGRANTthings to roles that do not exist on the destination and the whole restore will fail.--schema=public --schema=storagedumps just your application tables and the storage tables. Theauthschema stays on Supabase if you are following the middle path.- Run from a machine with the same Postgres major version as Supabase (currently 17). Mismatched
pg_dumpversions can produce dumps the destination cannot read.
Step 3: Restore to Layerbase
psql \
"postgresql://layerbase:<password>@your-host.cloud.layerbase.dev:5432/app?sslmode=require" \
< supabase-dump.sqlFor a small database (under 1GB) this takes a couple of minutes. For larger databases use pg_restore with -j 4 for parallel restore:
# If you used pg_dump -Fc (custom format) instead of plain SQL:
pg_restore \
--no-owner --no-acl -j 4 \
-d "postgresql://layerbase:<password>@your-host.cloud.layerbase.dev:5432/app?sslmode=require" \
supabase-dump.dumpStep 4: Repoint Lovable
In your Lovable env settings, change DATABASE_URL to the new connection string. If your generated code uses the Supabase JS client for database access (not just auth), replace it with postgres or pg. See Connect a Postgres database to a Lovable app for the exact code.
Step 5: Verify and cut over
Before you flip production, run the app against the new database in a staging environment. Spot-check the rows count for each table:
select schemaname, relname, n_live_tup
from pg_stat_user_tables
order by n_live_tup desc;The counts should match Supabase. If they do, flip production by deploying the env var change. If they do not, find out why before flipping.
Keeping auth working during the move
If you are leaving Supabase auth in place, the auth table lookups still go to Supabase. That is fine. Your application code that hits the database now goes to Layerbase. The two are decoupled.
The piece that needs attention is foreign keys. If your application tables have FOREIGN KEY (user_id) REFERENCES auth.users(id), that foreign key cannot exist on Layerbase because the auth.users table is on Supabase. Drop those foreign keys:
alter table public.posts drop constraint if exists posts_user_id_fkey;
alter table public.comments drop constraint if exists comments_user_id_fkey;
-- and so on for each table that references auth.usersYou lose database-enforced referential integrity for the user reference. You can replace it with application-level checks (validate the user exists before insert) or accept the looser coupling. Most teams accept it because the auth side controls user creation.
The user IDs themselves stay the same (UUIDs from Supabase). All your user_id columns continue to work.
Row-level security policies that reference auth.uid() need to be reimplemented in your application code, because auth.uid() is a Supabase-specific function. This is the part that takes the most work. In practice, most Lovable apps use RLS lightly (a few "user can only see their own rows" policies) and replacing those with application-level filters takes an afternoon.
What you do not need to migrate
A short list to keep the scope clear.
- Auth users. Stay on Supabase, do not migrate. The whole point of the middle path is that you do not have to.
- Storage objects. If you use Supabase storage and want to leave it, that is a separate migration. Most teams leave it in place for the same reason as auth: the cost is low and the migration is annoying.
- Edge functions. Lovable's deploy story does not depend on Supabase edge functions. If you wrote any (you probably did not), they need to be reimplemented as API routes in your Lovable app.
- Realtime subscriptions. If you use Supabase realtime, you will need a replacement. Postgres LISTEN/NOTIFY plus a thin WebSocket layer works. Or skip it and use polling. Realtime is the part of Supabase that is hardest to leave; if you depend on it heavily, the middle path may not be the right one.
Wrapping up
The short version of "do I move and how":
- If the Supabase bill is $25 per month and you are happy with the experience, do not move. There is no win.
- If the bill is climbing past $100 per month and growing, run the migration. It pays for itself.
- Move the database first. Keep auth on Supabase initially. This is the lowest-risk, highest-leverage step.
- Migrate auth only if you have a specific reason (better signup flow, MFA features, multi-tenant requirements). Most Lovable apps never need to.
The destination is your choice. Layerbase Cloud gives you plain managed Postgres with room to add Redis and Qdrant later if your app grows that way. Neon gives you branching for preview deploys. A $5 VPS gives you a fixed bill and operational work. All three are valid, and the migration steps above are identical for any destination that speaks Postgres.
For local development against the new setup, SpinDB runs Postgres locally and accepts the same dump file. Develop against a local copy, deploy against the cloud one.