The problem: npm run build completes without issue on a local machine, but the exact same command fails during a Forge deployment on the server.
Environment: Laravel 11, Vite 5, Laravel Forge, Ubuntu 22.04 server, Node via NVM
The Error
sh: 1: vite: not found
npm ERR! code ELIFECYCLE
npm ERR! errno 127
==> Deployment failed
Why It Happens
This class of error almost never means Vite is actually broken — it means the server's shell environment differs from a local machine's in ways that quietly break the build. Three causes cover nearly every case:
1. Node isn't in the Forge deploy script's PATH. Forge's deploy script often runs in a non-interactive shell that doesn't automatically source nvm, so even though Node is installed and works fine when you SSH in manually, the deploy script itself can't find it — leading to "vite: not found" even though node_modules/.bin/vite clearly exists.
2. node_modules wasn't installed for this release. If npm ci/npm install is missing from the deploy script, or runs before a fresh git pull gives it an updated package.json, the build step runs against stale or absent dependencies.
3. Memory limits on smaller Forge server plans. Vite's build process can exceed the RAM available on budget-tier droplets, causing the Node process to get OOM-killed mid-build — this shows a different, less obvious error (or none at all, just a non-zero exit code) compared to the "not found" case above.
The Fix
Make sure the deploy script explicitly sources NVM before running any npm commands, since Forge's script shell doesn't do this automatically:
export NVM_DIR=~/.nvm
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm use 20
cd /home/forge/app.com
npm ci
npm run build
Confirm npm ci (not just npm install) runs on every deploy, right after git pull, so dependencies always match the current package-lock.json:
git pull origin $FORGE_SITE_BRANCH
npm ci
npm run build
If the server is on a low-RAM plan and the build is being OOM-killed, either bump the server's memory tier or add swap space as a stopgap:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Step-by-Step
- Open the failed deploy in Forge's dashboard and read the exact error line — "not found" vs a silent non-zero exit tells you which cause to chase first.
- Add the NVM sourcing lines shown above to the top of your deploy script, before any npm command.
- Confirm
npm ciruns after everygit pull, not just on first setup. - SSH into the server and run
free -hto check available RAM if the build fails without a clear "not found" message. - Add swap space (or upgrade the server plan) if memory is the bottleneck.
- Trigger a fresh deploy and watch the log end-to-end to confirm
npm run buildcompletes with a 0 exit code.
Edge case: If your team uses different Node versions locally (one dev on Node 18, another on Node 20) and neither matches what's pinned on the Forge server, a build can pass for one teammate and fail for another even before it ever reaches Forge — add an .nvmrc file to the repo and make sure the Forge server's default Node version matches it exactly, so "works on my machine" mismatches get caught before deploy rather than during it.
Still failing after fixing NVM sourcing and dependencies? Reach out on the Contact page and I'll help review your server config.