Help Center

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.conf and requirements*.txt that point at a private mirror (for example package-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 committed node_modules/ or .venv/ is left out when the build installs dependencies itself (Nixpacks, buildpacks, or a Dockerfile that runs an install command). replit.md and 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:

  1. Open the build log (or Project → AI Debug) and choose Commit this clean-up / Apply fix.
  2. Review the diff for each file (the lockfile diff shows exactly which URLs change).
  3. 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.

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 of vite.config.ts. Production builds never use it, so MarvX leaves the import out of the build. A build no longer fails with Cannot find package 'lovable-tagger' when devDependencies aren't installed.
  • One package manager: Lovable repos often contain both bun.lockb and package-lock.json, and the build would install with one tool and build with the other. MarvX uses the one named in package.json's packageManager field. Otherwise it uses npm when package-lock.json exists, 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/ and lovable.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.0 and the PORT environment 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 start script in package.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 leftover bun.lockb for 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.