How to Deploy a Replit App to Production (And Whether to Stay on Replit)
Replit makes deployment easy — but 'deployed' isn't the same as 'production-ready.' Here's how to harden your Replit app for real users, and when it makes sense to migrate off Replit hosting.
Replit will deploy your app for you. That's part of the problem.
"Deployed" and "production-ready" aren't the same thing. Here's what to fix, and how to decide whether Replit hosting is enough or you need to move.
1. Decide: stay on Replit or migrate?
This is the first decision you need to make, because it affects everything else.
Stay on Replit if:
- Your app has low to moderate traffic (under ~1,000 daily users)
- You don't need custom server configuration
- You want the simplest possible deployment workflow
- Your app is a straightforward web app without complex infrastructure needs
Migrate off Replit if:
- You need consistent uptime guarantees (Replit's free tier sleeps after inactivity)
- Your app requires WebSocket connections, background workers, or cron jobs
- You need fine-grained control over your server environment
- You're hitting performance limits or cold start issues
If you're migrating, the most common targets are Railway (closest to Replit's simplicity), Vercel (best for Next.js), or Fly.io (best for custom server setups).
2. Extract and audit your code
Whether you stay or go, get your code into a proper git repository:
git init
git remote add origin https://github.com/your-username/your-app.git
git add .
git commit -m "Initial export from Replit"
git push -u origin main
Now audit what Replit Agent generated. Common issues:
- Express with no middleware — Replit often generates bare Express apps with no helmet, no CORS config, no rate limiting
- Inline database queries — SQL or MongoDB queries directly in route handlers with no validation
.replitandreplit.nixfiles — These are Replit-specific config. They won't work elsewhere and can be removed if migrating
3. Add input validation
Replit-generated apps frequently accept form input and write it straight to the database. This is a security vulnerability.
For every route that accepts user input:
const { z } = require("zod");
const createPostSchema = z.object({
title: z.string().min(1).max(200),
body: z.string().min(1).max(10000),
});
app.post("/api/posts", (req, res) => {
const result = createPostSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: "Invalid input" });
}
// Now safe to use result.data
});
4. Secure your secrets
Replit stores secrets in its own Secrets panel, which sets them as environment variables. This works, but audit what's in there:
- Remove any secrets you're not using
- Make sure database connection strings use SSL in production
- If you've moved to a new host, configure the same variables in your new provider's dashboard
- Never commit a
.envfile to git
5. Add authentication properly
Replit Agent often generates basic auth that handles sign-up and login but skips:
- Session expiry — tokens that never expire are a security risk
- Password hashing — make sure you're using bcrypt, not storing plain text
- Route protection — add middleware that checks auth on every protected route
- CSRF protection — especially important for form submissions
function requireAuth(req, res, next) {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return res.status(401).json({ error: "Unauthorized" });
try {
const user = verifyToken(token);
req.user = user;
next();
} catch {
return res.status(401).json({ error: "Invalid token" });
}
}
app.use("/api/protected", requireAuth);
6. Set up error monitoring
Replit's console shows logs while you're watching, but you won't be watching at 2am when something breaks. Install monitoring:
npm install @sentry/node
Configure it to capture unhandled exceptions and rejected promises. Set up alerting so you get notified immediately when errors spike. For more on common crash causes, read why AI-generated apps keep crashing.
7. Optimise your database
Replit apps often use SQLite (fine for development, risky for production) or connect to an external database without optimisation.
If using SQLite: Migrate to PostgreSQL (Supabase or Neon are good free options). SQLite can't handle concurrent writes from multiple users.
If using PostgreSQL/MySQL:
- Add indexes on columns you query frequently
- Add pagination to any endpoint that returns lists
- Use connection pooling to avoid exhausting database connections
8. Set up CI/CD
If you've migrated off Replit, set up automatic deployments:
- Push to GitHub
- CI runs your tests and linter
- On passing, deploy automatically to your hosting provider
If you're staying on Replit, use Replit's built-in deployment feature but still push to GitHub as a backup. Your code should never exist only on Replit.
The deployment checklist
Before going live:
- [ ] Decision made: stay on Replit or migrate
- [ ] Code in a proper git repository (not just on Replit)
- [ ] Input validation on every route that accepts user data
- [ ] Secrets audited and properly configured
- [ ] Authentication with session expiry and route protection
- [ ] Error monitoring installed with alerting
- [ ] Database suitable for production (not SQLite)
- [ ] Rate limiting on public endpoints
- [ ] HTTPS enforced on custom domain
- [ ] Tested on real mobile devices
Need help?
Moving a Replit app to production — especially if you're migrating hosting — involves a lot of moving parts. If you want expert eyes on your app before launch, request a free audit. We specialise in Replit apps and will give you a clear, prioritised action plan.
For the full production readiness checklist covering all vibe-coded apps, see our complete production checklist.
Get articles like this in your inbox
Practical tips on shipping vibe-coded apps. No spam.
Keep reading
Want to know where your app stands?
Get a free 5-point security snapshot from our dev team — no strings attached.