Free launch audit — a senior engineer reads your repo and tells you exactly what's blocking launch.Claim yours
Production engineering

Replit to production: what to keep, what to move, and when

Replit Agent builds and hosts your app in one place. Here is how to tell whether that still fits once real users arrive, what to fix regardless, and how to move the database or the hosting without losing anything.

GEGen2ProdProduction engineering

5 min read

A single shipping container being lifted from a dock onto a larger vessel at dusk

Replit Agent is the only AI builder that gives you the whole thing in one place: the editor, the runtime, the database, the secrets, the deployment. For a first version that is a real advantage — there is nothing to wire up and nothing to configure.

It is also why "Replit to production" is a different question from the same question about Lovable or Bolt. Those tools hand you a repo and leave; Replit is also your host, and the decision about whether it should stay your host is one you have to make deliberately. This post is about making it well.

What Replit Agent actually built

Look before deciding anything. The typical shape:

LayerWhat Agent generatedWhat to check
ServerNode/Express or Python Flask/FastAPI, often in one fileAuth middleware, input validation, CORS
ClientReact with Vite, served by the same processNothing unusual — usually fine
DataReplit's Postgres (Neon-backed) or Replit DB key-valueWhich one, and whether the shape fits
SchemaPushed with drizzle-kit push or created by handNo migration history
SecretsReplit SecretsFine — but see the fallback pattern below
HostingReplit Deployments: Autoscale or Reserved VMCold starts, cost curve, region

The rows that need fixing whether or not you move are the server and the schema. The rows where you have a decision to make are data and hosting.

Fix these regardless of where it runs

Every route trusts the caller

Generated Express routes look like this:

app.delete("/api/projects/:id", async (req, res) => {
  await db.delete(projects).where(eq(projects.id, req.params.id));
  res.json({ ok: true });
});

Nothing checks who is asking. Anyone who can reach the URL can delete any project. Add an auth middleware that verifies the session and attaches the user, then scope every query to that user:

app.delete("/api/projects/:id", requireUser, async (req, res) => {
  const { rowCount } = await db
    .delete(projects)
    .where(and(eq(projects.id, req.params.id), eq(projects.ownerId, req.user.id)));
  if (!rowCount) return res.status(404).end();
  res.json({ ok: true });
});

Do the same for every route that reads or writes user data. Agent-generated apps routinely have twenty or thirty routes and it is rare for any to be protected before someone goes through them.

CORS is *

app.use(cors());

That allows every origin. Restrict it to your own domain:

app.use(cors({ origin: ["https://yourapp.com"], credentials: true }));

The session secret has a fallback

Search the server for this shape:

secret: process.env.SESSION_SECRET || "dev-secret"

If the variable is ever missing in production — a redeploy to a new environment, a renamed secret — every session is signed with a string that is in your repo. Remove the fallback and let the app refuse to start without it.

The schema has no history

drizzle-kit push applies your schema file straight to the database. It is fast, and it means there is no record of what changed when and no way to roll a schema change back. Switch to generated migrations:

npx drizzle-kit generate   # writes a versioned SQL file
npx drizzle-kit migrate    # applies pending ones

Commit the migrations directory. From now on a schema change is a file in a pull request that someone can read.

Decision one: the data store

If Agent used Replit's Postgres, you have a real database and the question is only whether to keep it where it is. It is Neon underneath, so moving to your own Neon or Supabase project is a pg_dump and pg_restore with a new connection string — worth doing if you want billing, backups and access control under your own account, and not urgent otherwise.

If Agent used Replit DB — the key-value store you access with db.get() and db.set() — you have a decision to make sooner. Replit DB is a convenient dictionary. It has no relations, no indexes, no transactions and no way to query by anything except the key. Agent reaches for it because it needs zero setup, and it works fine right up until you have users whose records relate to other users' records.

Signs it is time to move:

  • You are storing JSON arrays under a key and filtering them in application code.
  • Two requests can race and one overwrites the other's change.
  • A "list all" operation is getting visibly slower.

Moving is a one-off script: read every key, insert into a Postgres schema that models the relations properly, verify counts, switch the connection. Nothing is lost, and the app gets indexes, constraints and transactions for free. Our database and query optimisation work is mostly this, and the shape of the resulting schema matters more than which Postgres host it lands on.

Decision two: the hosting

Replit Deployments come in two shapes, and Agent picks one for you:

AutoscaleReserved VM
Idle behaviourScales to zero; the next request waits for a cold startAlways on
CostPer compute unit — grows with trafficFixed monthly
Good forLow, bursty traffic; internal toolsSteady traffic; anything latency-sensitive
Watch forCold starts hurting real users; bill climbing with successPaying for idle

Neither is wrong. What is wrong is not knowing which you are on and why.

Stay on Replit when traffic is modest, cold starts are not hurting anyone you charge, and the bill is a rounding error. Many apps live here happily and moving them would be work for its own sake.

Move when one of three things is true: a cold start is the first thing a paying user experiences, the compute bill is growing faster than revenue, or you need something the platform does not offer — a specific region, a long-running background worker, a compliance requirement about where data lives.

If you move, the destination for an Express or Flask app with a Postgres database is usually Fly.io, Railway or Render, all of which take a Dockerfile or detect the framework directly. The move is:

  1. Export the repl to GitHub if it is not already there.
  2. Add a Dockerfile or let the host detect the runtime.
  3. Recreate every secret from Replit Secrets on the new host.
  4. Point the app at the database (moved or not — see decision one).
  5. Deploy to a preview URL, test with a real account, then switch DNS.

The app does not change. What changes is that you now have a deploy pipeline, preview environments and a rollback, which Replit's one-click deploy never gave you — deployment and CI/CD is the longer version of what that buys.

Whichever you choose: watch it

Replit's console shows logs while you are looking at it. A production app needs someone told when it breaks at 3am. Add error tracking and an uptime check on the paths that matter — sign-in, checkout, whatever earns money — and route the alert somewhere you will see it. This applies identically on Replit and off it.

The order to do it in

  1. Fix the routes, the CORS and the session secret. This is a day's work and it is the part that can hurt you tomorrow.
  2. Introduce migrations, so the next change is recorded.
  3. Measure: cold-start latency as a real user sees it, and the monthly bill against revenue.
  4. Decide on data and hosting from the measurements, not from a feeling.
  5. Add monitoring wherever you end up.

If you want the measuring and the ranked list done for you, the free launch audit is a senior engineer reading your repl and coming back within 48 hours with each finding reproduced and a fixed price against each fix. What we fix in Replit apps covers what else we typically find.

Share

Your AI-built product deserves a real launch.

Start with the free audit. In 48 hours you'll know what's broken, what each fix costs, and whether it's even worth doing — before you've spent a dollar.

Free audit, no card · Fixed price before we start · Your code stays yours