Explore why Lovable production builds differ from previews, fix production issues, and apply best practices for stability.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Production builds fail differently than Previews in Lovable because Previews run in a development-like, forgiving environment (with different env vars, caching, runtime, and build flags) while Publish runs a strict, production build and hosting environment that exposes differences in environment variables, SSR vs client code, bundler/tree-shaking optimizations, case-sensitive filesystems, and runtime versions. In short: Preview ≠ Production — subtle config, runtime, and asset differences explain why something works in Preview but breaks after Publish.
Preview in Lovable is optimized for quick feedback: it uses build modes and environment values suitable for iterative work, serves assets less aggressively, and may skip or relax some production optimizations. Publish — different build flags, minification, code elimination, stricter asset resolution, production-only environment variables (Secrets), and the cloud runtime (case-sensitive file system, different Node version, server vs edge differences). Those changes reveal problems that Preview masks.
// Please run these checks inside Lovable and return findings (do NOT change files).
// 1) Build logs: open the Preview build log and the Publish/Production build log and show both.
// 2) Files to inspect: open these files and copy the relevant sections.
// - package.json -> "scripts" and "engines"
// - next.config.js OR vite.config.js OR astro.config.mjs (if present)
// - any adapter/deploy config: vercel.json, netlify.toml, lovable.json (if exists)
// - src/ (or app/) search for "process.env." usages and list files + the exact keys used
// - public/ asset references and any basePath or assetPrefix settings
// 3) Secrets vs .env: list keys configured in Lovable Secrets UI and list .env* files present in repo (do NOT print secret values).
// 4) Node/Runtime issues: report any imports of Node-only modules (fs, child_process, net, tls) that appear in client-side code.
// 5) Case-sensitivity: list any import paths whose filename casing does not exactly match the filesystem name.
// 6) Build warnings/errors: capture any warnings about minification, tree-shaking, or unresolved imports from the production build log.
// 7) Server functions: list files under /api, /functions, or server/ and note runtime type (SSR, edge, lambda) if configured.
// Return a concise comparison: key differences between Preview and Publish logs and which file/setting points to each difference.
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
Apply small, targeted runtime fixes and diagnostics inside Lovable (no terminal) so you can reproduce and fix production-only failures quickly: add a lightweight production diagnostics module, surface errors to a persistent sink (console + your error service), confirm production secrets in the Lovable Secrets UI, pin Node/engines in package.json, and — only if you need custom build steps — export to GitHub for terminal work. Paste the prompts below directly into Lovable chat to make the edits.
// Lovable prompt: create a production diagnostics file and import it from the app entry
// Create file src/diagnostics/production.ts with the code below
// Then detect the app entry (pages/_app.tsx, src/main.tsx, src/index.tsx, src/App.tsx) and add an import at the top of that file:
// import '../diagnostics/production';
// If multiple are present, update the one that bootstraps the app.
Create file: src/diagnostics/production.ts
// Production diagnostics - safe, low-noise, no third-party deps
if (typeof window === 'undefined') {
// Node/runtime side
console.log('PROD_DIAG: server-side starting', {
nodeVersion: process.version,
env: process.env.NODE_ENV,
envKeysCount: Object.keys(process.env || {}).length,
});
process.on('uncaughtException', (err) => {
// send to console so Lovable logs capture it
console.error('PROD_DIAG: uncaughtException', err && err.stack ? err.stack : String(err));
});
process.on('unhandledRejection', (reason) => {
console.error('PROD_DIAG: unhandledRejection', reason);
});
} else {
// Browser-side
console.log('PROD_DIAG: client-side start', { env: process.env.NODE_ENV });
window.addEventListener('error', (e) => {
console.error('PROD_DIAG: window.error', e.error || e.message);
});
window.addEventListener('unhandledrejection', (e) => {
console.error('PROD_DIAG: unhandledrejection', e.reason);
});
}
// Lovable prompt: add a small retry wrapper and replace direct fetch/axios calls where failures happen
// Create src/lib/retry.ts and patch/replace call sites to use it.
Create file: src/lib/retry.ts
// Simple retry wrapper used by server/client code
export async function retry<T>(fn: () => Promise<T>, attempts = 2, delayMs = 500): Promise<T> {
let lastError;
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
lastError = err;
// eslint-disable-next-line no-await-in-loop
await new Promise((res) => setTimeout(res, delayMs));
}
}
// Add explicit message to help production logs
throw new Error(`RETRY_FAILED after ${attempts} attempts: ${lastError && lastError.message ? lastError.message : String(lastError)}`);
}
// Then find files that call external APIs (examples: src/api/*.ts, pages/api/*.ts) and change:
// const res = await fetch(url)
// to:
// const res = await retry(() => fetch(url))
// Lovable prompt: instruct the developer to add production secrets using Lovable Secrets UI and update code to fail fast with clear log messages
// Add instructions in the project and modify code to validate required secrets at startup.
Update file: src/diagnostics/production.ts (append)
if (typeof process !== 'undefined') {
const required = ['SENTRY_DSN', 'DATABASE_URL', 'API_KEY']; // adjust to your app's keys
required.forEach((k) => {
if (!process.env[k]) {
console.error(`PROD_DIAG: missing required secret ${k}. Please add it in Lovable Secrets UI for the production environment.`);
}
});
}
// Lovable action instructions for the user (not terminal):
// Open the Lovable Secrets panel and add the production values for the keys listed above (SENTRY_DSN, DATABASE_URL, API_KEY).
// After adding, use Lovable Preview with the production environment (or Publish) to reproduce.
// Lovable prompt: update package.json to add an engines field and a postinstall safety message
// Modify package.json at project root
Update file: package.json
// add or modify the "engines" block, do not change other fields
"engines": {
"node": ">=18 <21"
},
"scripts": {
// keep existing scripts, but add a safety script if appropriate
// e.g., "start": "node server.js"
}
If you hit a build need Lovable cannot run (custom native binaries or specialized build steps), export/sync to GitHub and run the build in your CI or locally — mark that step outside Lovable (terminal required) when you paste into the Lovable chat.
Keep production stable by treating Lovable previews as a fast feedback loop, then enforcing runtime guards, explicit secrets in Lovable Cloud, deterministic runtime + dependency metadata, health/readiness endpoints, structured logs/errors, and a simple export-to-GitHub workflow for anything that needs local/CI control. Implement small, deterministic runtime checks and observability inside the repo (via Lovable edits), manage secrets with the Secrets UI, and use GitHub export only for things that require a terminal (locks, native builds, heavy CI).
// Prompt A: Add runtime env validation, .env example, engines, and .nvmrc
// Edit root package.json: add "requiredEnv" and "engines" entries.
// Create src/utils/validateEnv.js and .env.example and .nvmrc
Please:
- Update package.json (root) to include these fields (add or merge):
"requiredEnv": ["DATABASE_URL","JWT_SECRET"],
"engines": { "node": "18.x" }
- Create file .nvmrc at project root with this content:
18
- Create file .env.example at project root with this content:
DATABASE_URL=
JWT_SECRET=
OTHER_OPTIONAL=
- Create file src/utils/validateEnv.js with this content:
// src/utils/validateEnv.js
const pkg = require('../package.json');
// fall back to the array in package.json or use an explicit small default
const required = Array.isArray(pkg.requiredEnv) ? pkg.requiredEnv : ['DATABASE_URL','JWT_SECRET'];
const missing = required.filter(k => !process.env[k]);
if (missing.length) {
console.error('Missing required env vars:', missing.join(', '));
// crash early so preview/production surfaces the problem
throw new Error('Missing required env vars: ' + missing.join(', '));
}
module.exports = true;
- If your app has a server entry (e.g., src/server.js, src/index.js, or next.config.js/pages/_app.js), add at the very top:
require('./utils/validateEnv');
// If you want, detect common locations and insert import lines for those files.
// Prompt B: Add simple health endpoints (static fallback + example dynamic)
// Create public/health.json and example handlers for Next.js and Express
Please create:
- public/health.json
{"ok":true,"uptime":0}
- src/pages/api/health.js (for Next.js)
export default function handler(req, res) {
res.status(200).json({ ok: true, uptime: process.uptime() });
}
- src/api/health.js (example Express handler)
module.exports = function healthHandler(req, res) {
res.status(200).json({ ok: true, uptime: process.uptime() });
};
// Prompt C: Add a tiny structured logger and readme instructions for Secrets UI
// Create src/middleware/requestLogger.js and update README with secrets steps
Please create:
- src/middleware/requestLogger.js
// src/middleware/requestLogger.js
module.exports = function requestLogger(req, res, next) {
const log = {
time: new Date().toISOString(),
method: req.method,
path: req.url,
// avoid logging secrets
};
console.log(JSON.stringify(log));
next();
};
- Update README.md (append) with a short section about Lovable Secrets UI:
"Add production secrets in Lovable Cloud > Secrets: set DATABASE_URL and JWT_SECRET. Do NOT commit real secrets. Ensure the keys match package.json.requiredEnv. For Sentry or other SDK installs, export to GitHub and add the SDK there (terminal required)."
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.