Fixed: Vite Manifest Version Mismatch After Laravel Forge Deploy

The problem: Right after a Forge deployment finishes, the site throws a manifest version error, and pages that reference Vite assets fail to load entirely until a manual rebuild.

Environment: Laravel 11, Vite 5, Laravel Forge, zero-downtime deployment

The Error

● ● ●  browser error page
Illuminate\Foundation\ViteManifestNotFoundException: Vite manifest not found
at: /var/www/app/public/build/manifest.json

(occurs intermittently right after deploy, resolves after ~30s)

Why It Happens

Forge's zero-downtime deployment works by building the new release in a separate directory, then atomically swapping a symlink to point the live current folder at it once everything's ready. The problem is ordering: if your deploy script runs npm run build after the symlink swap (or if the build step is slow and the symlink swap happens first due to script structure), there's a window where the new release is already live, but public/build/manifest.json hasn't been generated yet — because the asset build for that specific release hasn't finished.

This is purely a race condition in deploy script ordering, not a code bug. It's especially common on projects that were migrated to Forge's newer zero-downtime deploys from an older, sequential deploy setup, since the old deploy script's step order assumed a single linear timeline that no longer matches how zero-downtime deploys actually execute.

The Fix

Reorder your Forge deploy script so npm run build completes fully before the release is activated (before any artisan commands that assume the site is live, and definitely before the symlink swap):

Forge Deploy Script
cd /home/forge/app.com
git pull origin $FORGE_SITE_BRANCH

$FORGE_COMPOSER install --no-dev --no-interaction --prefer-dist --optimize-autoloader

npm ci
npm run build

( flock -w 10 9 || exit 1
    echo 'Restarting FPM...'
    ( sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock
)

$FORGE_PHP artisan migrate --force

The key detail is that npm run build must run before the FPM reload / symlink activation, not after — building assets after the site is already serving the new release is what creates the gap where manifest.json doesn't exist yet.

As a safety net, also add a deploy-time check that fails the deployment loudly if the manifest is missing, rather than letting it silently go live broken:

if [ ! -f public/build/manifest.json ]; then
    echo "Vite manifest missing — aborting deploy"
    exit 1
fi

Step-by-Step

  1. Open your site's Deployment Script in the Forge dashboard.
  2. Move npm ci && npm run build so it runs before any FPM reload or symlink-related step.
  3. Add the manifest existence check shown above right after the build step, so a broken build fails the deploy instead of going live.
  4. Trigger a manual deploy and watch the Forge deploy log to confirm the build completes before the site restarts.
  5. Immediately after deploy finishes, hard-refresh the site and confirm no manifest error appears.
  6. Repeat for a second deploy to confirm the race condition is actually gone, not just avoided by timing luck once.

Edge case: If you're using Forge's "Quick Deploy" (auto-deploy on git push) alongside a slow npm run build step (large dependency tree, no caching), pushing multiple commits in quick succession can queue overlapping deploys where the second deploy's build starts before the first one's symlink swap finishes — consider disabling Quick Deploy or adding a deploy lock if your team pushes frequently during active development.

Still seeing manifest errors after reordering your deploy script? Reach out on the Contact page and I'll help review your setup.

Advertisement
Advertisement