Deploying
Deploying from Replit or Lovable
Deploy an app you built on Replit or Lovable — what MarvX fixes automatically on every build (Replit's private npm/PyPI mirrors, Replit files and caches, Lovable's lovable-tagger plugin, two lockfiles, Vite SPA routing), the VITE_ variables to set, recommended repository hygiene, and how Apply fix commits the clean-up for you.
Apps exported from Replit or Lovable (lovable.dev), or synced to GitHub from them, usually deploy as-is on MarvX. Both tools leave a few things in the repository that only work inside their editor; MarvX handles them for you.
The classic failure: ENOTFOUND package-firewall.replit.local
On Replit, packages are downloaded through Replit's private mirror, and the lockfile records those URLs:
"resolved": "http://package-firewall.replit.local/npm/three/-/three-0.185.1.tgz"Outside Replit that host doesn't exist, so npm ci fails with npm error code ENOTFOUND … getaddrinfo ENOTFOUND package-firewall.replit.local. Setting a registry environment variable doesn't help: npm uses the URLs in the lockfile as they are.
What MarvX cleans up automatically (every build)
Before each build, MarvX cleans the copy of your code it builds. Your repository is not changed.
- Lockfiles (
package-lock.json,npm-shrinkwrap.json,yarn.lock,pnpm-lock.yaml): dependency URLs on private or unreachable hosts are pointed at the public registry (registry.npmjs.org). The package path and the integrity hashes are kept, so every package is still verified. When a URL has no safe public equivalent, MarvX removes that one URL and the package manager fetches the pinned version from the public registry. Nothing else in the file changes. - Package-manager settings: lines in
.npmrc,.yarnrc,.yarnrc.yml,pip.confandrequirements*.txtthat point at a private mirror (for examplepackage-proxy.replit.com,*.replit.local,localhost, private IP addresses) are removed. Settings for real registries (GitHub Packages, your company's public registry) and their tokens are left alone. - Replit files and caches are left out of the build:
.replit,replit.nix,.breakpoints,.upm/,.pythonlibs/and__pycache__/. For repos made on Replit, also.cache/,.local/and.config/(only when it holds nothing but Replit's tool settings). A committednode_modules/or.venv/is left out when the build installs dependencies itself (Nixpacks, buildpacks, or a Dockerfile that runs an install command).replit.mdand everything else stays.
The build log shows what was done, for example:
==> MarvX cleaned up your repo for this build (your repository is unchanged — use "Apply fix" in AI Debug to commit this):
• package-lock.json: rewrote 412 lockfile URLs from package-firewall.replit.local to registry.npmjs.org
• ignored .replit, replit.nix, .upm/, .cache/Make it permanent: Apply fix
The build-time clean-up repeats on every deploy. To fix the repository itself:
- Open the build log (or Project → AI Debug) and choose Commit this clean-up / Apply fix.
- Review the diff for each file (the lockfile diff shows exactly which URLs change).
- Click Apply fix & redeploy. MarvX commits the selected changes as one commit to the app's branch through your workspace's GitHub connection, then deploys that commit.
- The commit is credited to your GitHub account, with a plain message such as
fix(deploy): use the public package registry in package-lock.json. - If auto-deploy is on, the push doesn't start a second deployment.
- The commit is credited to your GitHub account, with a plain message such as
If the branch is protected, or you prefer to review on GitHub, choose Open pull request. MarvX pushes the change to a fix/deploy-… branch and opens a PR; merging it deploys as usual.
For a broader clean-up, run Repository review in AI Debug. It covers .gitignore, .env.example, the README, the Node version, Replit and Lovable leftovers, and more, and you pick which items to commit.
The committed fix rewrites the lockfile URLs, removes committed caches (.upm/, .cache/, .local/, .pythonlibs/, __pycache__/, small committed node_modules/) and removes extra lockfiles. If a Vite app is set to Static, it also switches the build type to Nixpacks. It keeps .replit, replit.nix and .lovable/, so you can keep working in Replit or Lovable. The public registry also works from Replit.
Lovable apps (Vite + React, often Supabase)
A Lovable project is a Vite single-page app. Deploy it with the Nixpacks build type (the default). MarvX runs npm run build and serves dist/ on $PORT with an index.html fallback, so refreshing /dashboard or any other client-side route doesn't return 404. Static only serves files that are already committed. Lovable doesn't commit dist/, so a Static deploy shows a blank page, and AI Debug offers to switch the build type to Nixpacks. vite preview isn't needed as a start command.
What MarvX does on every build:
lovable-tagger: Lovable's development-only Vite plugin is imported at the top ofvite.config.ts. Production builds never use it, so MarvX leaves the import out of the build. A build no longer fails withCannot find package 'lovable-tagger'when devDependencies aren't installed.- One package manager: Lovable repos often contain both
bun.lockbandpackage-lock.json, and the build would install with one tool and build with the other. MarvX uses the one named inpackage.json'spackageManagerfield. Otherwise it uses npm whenpackage-lock.jsonexists, else bun. The other lockfiles are left out (Dockerfile builds keep theirs). NODE_ENV=production: if you set it, npm would skip devDependencies, including Vite itself. MarvX makes sure the build still installs them.- Node version: Vite 7 needs Node 20.19 or newer. Without a pinned version (
engines.node,.nvmrc), MarvX builds Vite 7 apps with Node 22. .lovable/andlovable.config*editor files are left out of the build. They stay in your repository, so Lovable's GitHub sync keeps working.
Supabase and other VITE_ variables
Vite bakes VITE_* variables into the JavaScript bundle at build time. Set them before you deploy, not afterwards. If your code reads a VITE_ variable that isn't set under Project → Environment and isn't in a committed .env, the build log warns with the exact names, for example:
==> Warning: your code reads VITE_SUPABASE_ANON_KEY, VITE_SUPABASE_URL, but they aren't set — Vite bakes VITE_* values into the bundle at build time.Add them under Project → Environment (for Supabase, copy them from your Supabase project's Settings → API), then redeploy. MarvX never invents values. The Supabase anon key is meant to be public; never put a service_role key in a VITE_ variable.
Recommended repository hygiene
- Commit one lockfile, made against the public registry. Regenerating it outside Replit also works: delete it, run
npm install, commit. - Add these to
.gitignore:node_modules/,.upm/,.cache/,.local/,.pythonlibs/,__pycache__/,.venv/. - Listen on
0.0.0.0and thePORTenvironment variable (Replit apps often use port 5000, MarvX defaults to 3000). Example:app.listen(process.env.PORT || 3000, "0.0.0.0"). - Replit Secrets don't come along. Add them under Project → Environment.
- Make sure a Node server app has a
startscript inpackage.json. Replit's Run button comes from.replit, which MarvX doesn't use. Vite single-page apps don't need one. - Keep one lockfile. For Lovable, that's usually
package-lock.json; Apply fix can remove a leftoverbun.lockbfor you.
Related
- AI build debugger: reads your repository as well as the log, and proposes one fix for every blocker it finds.
- Troubleshooting builds and deployments: other install and start-up errors.