Base44 lock-in is a stack of five distinct bindings, each with its own escape cost. The SDK binding: every API call goes through @base44/sdk to base44.com, a closed-source client with no abstraction layer. The entity binding: your data lives in a managed Postgres with no direct SQL access, no pg_dump, no portable schema export. The integration binding: invokeLLM, sendEmail, generateImage use platform-managed credentials you do not own. The rendering binding: your app assumes Base44's hosted shell and CSR runtime. The identity binding: users hold tokens issued by Base44's identity service with no migratable session state. The React component tree is portable; the SDK boundary, data layer, identity, and integration credentials are not. Realistic migration cost for a midsize SaaS: 200–600 engineering hours, $25,000–75,000 at agency rates.
Why this matters
Vendor lock-in discussions usually devolve into religious arguments. This one will not, because we will be specific about which parts of Base44 are locked in, which are portable, and what each costs to escape. The goal: give you a realistic dollar and time estimate for "what if we leave?" so the question stops being scary and starts being a budget item.
We have run six client migrations off Base44 in the last twelve months. The numbers in this article, from the lead engineer at Base44Devs, come from those engagements, not from theory.
The five bindings
Base44 lock-in is not one thing. It is five layered bindings, each with its own escape cost. Naming them precisely lets you plan migration phase by phase rather than as one terrifying lump.
- SDK binding — every API call goes through
@base44/sdkto base44.com. - Entity binding — your data is in a managed Postgres you don't have direct access to.
- Integration binding — your Stripe, Twilio, OpenAI calls go through platform-managed credentials.
- Rendering binding — your app assumes Base44's hosted shell and CSR runtime.
- Identity binding — your users authenticate against Base44's identity service.
Some are easier to escape than others. We will walk each.
Binding 1: the SDK
What's locked. Every entity CRUD call (Entity.list, Entity.create, etc.), every user auth call, every backend function invocation, every integration call. The SDK is closed-source and only works against base44.com.
What it looks like in code:
import { base44 } from "@base44/sdk";
const todos = await base44.entities.Todo.list({ user_id });
const me = await base44.User.me();
const result = await base44.functions.processPayment(payload);
Escape cost. Every SDK call site needs to be replaced with your new stack's equivalent. For a typical app:
- 50–200 entity call sites.
- 5–20 auth call sites.
- 10–50 backend function calls (these mostly become direct fetch to your new backend).
- 5–30 integration calls.
The replacements are mechanical but extensive. Sed/regex won't do it cleanly because the new stack's API doesn't map 1:1.
The pattern that helps. If you are still on Base44 and might leave eventually, wrap the SDK in a thin internal module today:
// src/lib/data.ts
import { base44 } from "@base44/sdk";
export const data = {
todos: {
list: (filter: TodoFilter) => base44.entities.Todo.list(filter),
create: (todo: TodoInput) => base44.entities.Todo.create(todo),
update: (id: string, patch: Partial<Todo>) => base44.entities.Todo.update(id, patch),
},
users: {
me: () => base44.User.me(),
},
};
Now your components import data.todos.list instead of base44.entities.Todo.list. When you migrate, you change one file. We have seen this single pattern cut migration time by 30–40%.
Realistic estimate to fully replace: 30–80 hours for a small app, 100–250 hours for a midsize app.
Binding 2: the entity layer (data)
What's locked. Your data lives in Base44's managed Postgres, but you do not have direct SQL access. Schemas are defined through the IDE. There is no pg_dump equivalent. Migration requires reading every record through the SDK and writing it to your new database.
What you can take.
- Record data, via
Entity.list()with pagination. - Field types, by inspection of the schema in the IDE.
- Created/updated timestamps.
- File URLs (but the files themselves live in Base44 storage, see binding 4).
What you cannot take.
- Indexes the platform set up for you.
- RLS rules (you must rebuild them in your new stack).
- Stored procedures or triggers (Base44 does not expose these).
- The exact column types — Postgres types and Base44 types don't always align cleanly.
The migration mechanics. A backend function that exports each entity to JSONL, then a script in your new environment that imports JSONL into your target schema:
// Export side (Base44 backend function)
export default async function handler(req: Request) {
const PAGE_SIZE = 1000;
let cursor: string | null = null;
const results = [];
while (true) {
const filter: Record<string, unknown> = {};
if (cursor) filter.created_date = { $gt: cursor };
const batch = await base44.entities.Todo.list(filter, "created_date", PAGE_SIZE);
if (batch.length === 0) break;
results.push(...batch);
cursor = batch[batch.length - 1].created_date;
}
return new Response(JSON.stringify(results), { status: 200 });
}
// Import side (Supabase, run from a script with service-role key)
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(URL, SERVICE_ROLE_KEY);
const records = await fetchExportFromBase44();
const BATCH = 500;
for (let i = 0; i < records.length; i += BATCH) {
const slice = records.slice(i, i + BATCH);
const { error } = await supabase.from("todos").insert(slice);
if (error) throw error;
}
Realistic estimate. 20–80 hours for a small app, 100–300 hours for a midsize app, including schema design, validation, and verification that record counts match.
Binding 3: integrations
What's locked. When you use base44.integrations.invokeLLM, sendEmail, generateImage, you are using credentials managed by the platform. You don't have an OpenAI API key; Base44 does. Same for the email sender, the image model, etc.
Escape cost. You sign up for the underlying providers directly, get your own keys, and replace every integration call with a direct API call from a backend function.
// Was:
const response = await base44.integrations.invokeLLM({ prompt });
// Becomes:
const openai = new OpenAI({ apiKey: Deno.env.get("OPENAI_API_KEY") });
const completion = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
});
const response = completion.choices[0].message.content;
Cost implication. Direct provider rates are sometimes cheaper than Base44's credit equivalent (you skip the platform markup), sometimes more expensive (you lose volume discounts the platform negotiates). Run the math before assuming either.
Realistic estimate. 10–40 hours for a typical app. Mostly straightforward; the gotchas are in error handling and rate limiting that the platform's wrapper hid.
Binding 4: rendering and runtime
What's locked. The Base44 IDE generates a React app that runs inside a Base44-hosted shell. The shell handles auth state, layout chrome, and platform integration UI. Custom domains are supported on Pro tier and above.
Escape cost. The React component tree itself is portable. The shell, the build pipeline, and the deployment infrastructure are not.
For a Next.js destination, the migration is:
- Export the React app from Base44.
- Set up a Next.js project with App Router.
- Drop the exported components into the Next.js routing structure.
- Replace the Base44 shell layout with Next.js layout.tsx files.
- Move backend function logic into Next.js API routes or Server Actions.
- Replace Base44's environment variable access with Next.js's
process.env. - Set up your CDN and deployment.
File storage migration. Files uploaded to Base44 storage have URLs at base44.app. After migration, those URLs still work (for now), but you should re-host the files at your own bucket so the platform can't yank them later. A backend function downloads each file and re-uploads to S3/R2.
Realistic estimate. 40–120 hours for a small app, 150–400 hours for a midsize app, depending on how much business logic the agent inlined into components vs. abstracted out.
Binding 5: identity
What's locked. Users authenticated to Base44 hold tokens issued by Base44's identity service. You do not have access to the password hashes or to the OAuth refresh tokens.
Escape cost. This is the trickiest binding. You cannot migrate sessions; users will need to re-authenticate after migration. For OAuth users (Google), they re-link their Google account to your new Auth0/Clerk/Supabase Auth identity. For email/password users, you have two options:
- Force password reset. Email every user a reset link, they set a new password against your new identity service. Simple, but it is a friction event for users.
- Lazy migration. When a user logs in for the first time after the migration, prompt them to set a new password (their old one no longer works). Spreads the friction over time.
For SSO users, you reconnect SSO at the IdP level.
Realistic estimate. 20–60 hours including communications, plus a few weeks of elevated support load.
Total realistic migration cost
Adding up the bindings for a midsize SaaS app (50+ entities, multi-tenant, 3–5 integrations, 1,000–10,000 users):
| Binding | Hours |
|---|---|
| SDK replacement | 100–250 |
| Data migration | 100–300 |
| Integrations | 10–40 |
| Rendering/runtime | 150–400 |
| Identity | 20–60 |
| Testing, QA, cutover | 50–120 |
| Total | 430–1,170 hours |
At $150/hour agency rate, that's roughly $65,000–175,000 for a full migration of a midsize app. At an internal team rate of $100/hour effective cost, $43,000–117,000. These are real numbers from real engagements.
For smaller apps, divide by 3–5x. For larger apps with custom integrations, multiply by 2x.
Read that as what the work weighs, not as what it sells for. It is an hours band multiplied by a generalist time-and-materials rate, and it assumes an app at the top of the size range. A scoped fixed-price migration is quoted on a different variable and lands in a different band — the two sections below reconcile them, and give you the order to spend the money in.
The order you rebuild in
The five bindings do not come off in the order they are listed above. They come off in dependency order, and getting that order wrong is how a migration acquires a second data migration halfway through.
Phase 0 — get the export out. Base44 exports two ways, as a project ZIP or a GitHub connection, and the platform's docs gate both behind the Builder plan ($40/month) or higher. What lands is the React frontend, your backend function source, and a JSON schema file describing your entities — not the SDK source and not a single database row. What the code export actually contains walks the nine things it leaves behind. Nothing downstream starts until this exists, and if you are on Free or Starter today, the first line item of your migration budget is one month of Builder.
Phase 1 — schema, before any application code. Rebuild the entity schema in the target's native types first. Every SDK replacement in the next phase gets written against this schema, so revising it later invalidates work already paid for. Phase 1 is also where the non-portable pieces from Binding 2 surface while they are still cheap: the indexes the platform set up for you, the RLS rules you have to restate, and the column types that do not map cleanly.
Phase 2 — the SDK boundary. The long pole. Every call site from Binding 1 gets replaced against the schema you just built. If you adopted the data.ts wrapper while still on the platform, this phase is one file rather than several hundred call sites — that is the whole argument for the wrapper, and it is worth doing even in the week you decide to stay.
Phase 3 — integrations, in parallel with phase 2. Integration replacement depends on neither the schema nor the SDK rewrite, so it does not belong on the critical path. Sign up for the underlying providers, hold your own keys, and rebuild the error handling and rate limiting the platform wrapper was hiding.
Phase 4 — identity, built early, switched last. Stand the new identity provider up whenever you like, but it cannot cut over ahead of the app: the moment a user authenticates against the new provider, their Base44 session is worth nothing. This is the only phase your users can see, so it is the one that needs a written comms plan rather than an engineering estimate.
Phase 5 — cutover, with both stacks live. Dual-run for at least a week with the old app still able to serve. Verify record counts and field values before you cancel anything on the platform side.
The strictly serial chain is 0 → 1 → 2. Phase 3 runs alongside phase 2. Phase 4 can be built at any point and only lands at the end. If a proposal in front of you sequences these differently, ask why.
What it costs to buy instead of build
Two variables move the price more than any binding on this page: where the app is going, and whether you are paying for hours or for a scope.
Where it is going. Each destination playbook on this site publishes its own estimate for a representative app, and the spread between them is not small:
| Destination | The playbook's own estimate |
|---|---|
| Lovable | 3–5 weeks · ~140 h |
| Bolt.new | 3–5 weeks · ~140 h |
| Replit | 3–6 weeks · ~160 h |
| Vercel | 4–10 weeks · ~240 h |
| Firebase | 6–10 weeks · ~280 h |
| Bubble | 6–10 weeks · ~280 h |
| Next.js + Supabase | 4–14 weeks, median 6.5 · ~360 h |
| Self-hosted | 10–16 weeks · ~480 h |
Same app, roughly 140 hours to roughly 480 hours, decided before anyone touches your code. The cheap end is cheap because it keeps a managed backend and an AI-assisted rebuild loop — you are changing vendors, not taking on operations. Self-hosting is the expensive end because its estimate includes the operational surface no other row carries: backups, monitoring, and on-call.
Hours or scope. Our own migrations off Base44 are priced by table count and complexity rather than billed by the hour, which is what makes them quotable before the work starts:
| Tier | Scope | Price | Elapsed |
|---|---|---|---|
| Small | ≤5 tables, ≤10 routes, single-role auth | $6,000 | 2–3 weeks |
| Medium | ≤20 tables, multi-role auth + Stripe | $12,000 | 4–5 weeks |
| Enterprise | Multi-tenant, SOC 2 / HIPAA-compatible, custom integrations | $25,000+ | quoted after scoping |
Size yourself against the table counts, not against the words small and midsize. The "small app" described earlier in this article — 10–20 entities, basic auth, one or two integrations — is a Medium by table count, not a Small. The midsize app the hours table prices — 50+ entities, multi-tenant, 3–5 integrations — sits above Medium entirely, which is exactly why Enterprise is quoted after a scoping call rather than listed: at that size the honest estimate band is too wide to publish, and it is the same band the 430–1,170 hour figure covers. Across the migrations behind those tiers the engagements run $6,000 to $50,000 with a median around $14,000; the Next.js and Supabase migration playbook breaks down which variables move a quote inside that band, the largest of them being how deeply the SDK is coupled into your components.
The practical read: the destination decision and the SDK-coupling depth are worth more attention than any other line item here, because they are the two that move the number by multiples rather than by percentages.
What you do not save when you leave
A common assumption: "we'll save the Base44 subscription." That's true. The cash savings are real. But you take on:
- Hosting costs (Vercel, AWS, etc.) — typically $50–500/month for an equivalent app.
- Database costs (Supabase, RDS) — $25–250/month.
- Third-party services you used to get bundled — email, image gen, monitoring — $100–400/month.
- Engineering ownership of the stack you now run.
The cost delta in cash is usually positive (you save money), but the operational cost delta in engineering attention is more nuanced. You trade "fight the platform's quirks" for "own the platform yourself." Which is better depends on your team.
When migration is the right call
We see five clear signals that migration pays for itself:
- Spending more than $1,500/month on Base44 for a single app. Migration payback is typically under 24 months.
- Regulatory requirements the platform cannot meet (HIPAA, SOC 2 for customers).
- Performance ceilings — you've optimized everything and INP is still 400ms+.
- A planned multi-region rollout — Base44's single-region hosting won't scale.
- Team friction with the AI agent — you've moved to the code editor for everything anyway, and the agent is no longer adding value.
If three or more apply, migrate. If one or two, run the math and the is base44 production ready framework.
When migration is the wrong call
Equally clear signals to stay:
- You are pre-product-market-fit. Migration time competes with finding-customers time, and customers always win.
- Your app is not the bottleneck. If the platform is fine and the bottleneck is sales or distribution, leave it alone.
- You have a clear ceiling on the app's scope. Internal tools, single-customer apps, and 50-user dashboards are fine to leave on Base44 indefinitely.
- You have not yet hardened the existing app. Migration of an unhardened app is harder than migration of a hardened one. Harden first.
Common lock-in mistakes
Treating "we'll just export the code" as a migration plan. Export is one of fifteen steps. The rest is the work.
Underestimating the SDK replacement. Counting call sites and assuming linear effort. Reality is 1.5–2x the linear estimate because of edge cases.
Skipping the data validation pass. Migrating data without a record-count and field-value verification is how you discover data loss six weeks later.
Cutting over without a fallback. Run dual-stack for at least a week. Be ready to revert.
Ignoring identity migration. Users having to reset passwords is annoying. Plan the communication and support load explicitly.
Forgetting external integrations. Webhook URLs change. Update every external system that points at your old Base44 webhook.
Lock-in mitigation if you stay
If you decide to stay on Base44 but want to reduce lock-in over time:
- Wrap the SDK in a project-local module (the
data.tspattern above). - Use direct API calls for integrations, not platform-managed wrappers, where possible.
- Mirror your data to an external store as a backup and to enable optionality later.
- Keep your auth flows portable — use Auth0 or Clerk via a backend function rather than Base44's native auth.
- Minimize unique-to-Base44 features in your codebase. Anything you can do generically, do generically.
These add some upfront cost but cut migration cost dramatically if you ever exercise the option.
Want us to scope a migration for you?
Our $497 audit produces a written migration plan with phased estimates, identified risks, and a recommended target stack. If you proceed with us, we discount the audit fee against the migration engagement. Order an audit or book a free 15-minute call to discuss the migration roadmap.
Related reading
- Base44 Limitations Explained — the structural constraints that drive migration decisions.
- Base44 SDK Reference — the API surface you'll need to replicate.
- Is Base44 Production Ready? — the explicit decision framework.
- What happens if Base44 shuts down — the worst-case scenario and how portability protects you.