Monorepo Deployment to Vercel
This guide explains how to deploy individual applications from a DevCentr monorepo to Vercel, specifically addressing the Asymmetric Build Strategy required for workspace-aware projects.
The "Asymmetry" Problem
In a DevCentr monorepo, you will encounter a deliberate inconsistency between how you build projects locally versus how Vercel builds them in the cloud.
| Local Machine (Root) | Vercel (Cloud) |
|---|---|
You run workspace-aware scripts from the root. |
Vercel identifies a "Root Directory" and builds inside it. |
Example: |
Example: |
Why this is necessary
While it feels inconsistent, this asymmetry is a requirement of Vercel’s optimized build pipeline:
-
Framework Detection: Vercel needs to "see" the
package.jsonof the specific app (e.g., Next.js or SolidStart) to apply the correct build optimizations and edge function configurations. -
Build Isolation: Deploying multiple apps from one repo requires Vercel to treat each one as a separate project with its own "Root Directory" reference point.
-
Caching efficiency: Vercel caches the
node_modulesand build artifacts relative to the designated Root Directory.
Configuration Guide
When registering your monorepo apps on Vercel, follow these steps to manage the asymmetry:
1. Root Directory Selection
Do not leave the Root Directory at the repository root.
-
Setting: In Vercel Project Settings, set the Root Directory to the specific app folder (e.g.,
apps/weborapps/docs). -
Effect: Vercel will now execute all commands as if it had
cd-ed into that folder first, but it will still be able to reference thepnpm-lock.yamlandnode_modulesin the actual repo root.
2. Build Commands
Since Vercel is scoped to the sub-folder, do not use the root convenience scripts.
-
Local Command:
pnpm build:web(Convenient shortcut for you). -
Vercel Build Command:
pnpm build(The script defined insideapps/web/package.json).
3. Monorepo Settings
Ensure Vercel recognizes the monorepo structure:
-
Ensure "Include Source Files outside of the Root Directory in the Build Step" is enabled (usually default).
-
This allows the sub-app to access shared packages or root configuration files.
4. SolidStart: Set the Nitro Vercel Preset (Critical)
If your app uses SolidStart (which uses Nitro under the hood), you must explicitly set the Nitro deployment preset to vercel. Without this, Nitro builds a generic Node server bundle in .output/ that Vercel cannot run, resulting in a 404 on all routes even after a "successful" build.
apps/web/vite.config.tsimport { defineConfig } from "vite";
import { nitroV2Plugin as nitro } from "@solidjs/vite-plugin-nitro-2";
import { solidStart } from "@solidjs/start/config";
const preset = process.env.VERCEL ? "vercel" : "node-server";
export default defineConfig({
plugins: [
solidStart({
server: {
preset,
},
}),
nitro(),
],
});
Why Nitro needs a preset: Nitro is a universal server engine that can target many hosts (Node, Vercel, Netlify, Cloudflare, etc.). Each host expects a different output directory format and entry point structure. The vercel preset produces the .vercel/output/ layout with a config.json, static assets, and serverless function files. Vercel’s build infrastructure looks for this layout specifically — if it doesn’t find it, the deployment has nothing to serve.
The VERCEL environment variable is automatically set to "1" on all Vercel build runners, so the condition is transparent. Local pnpm dev and pnpm build continue to use node-server.
| This applies to SolidStart projects only. Next.js and other frameworks handle their own Vercel integration internally and do not require this step. |
Summary of Asymmetry
Always remember: Local scripts are for humans (root convenience); Vercel scripts are for machines (folder scoping).
If a build fails on Vercel but works locally, verify that your Root Directory setting matches the app you are trying to deploy.
If a build succeeds on Vercel but every URL returns 404, you are almost certainly missing the framework-specific deployment preset (see §4 above).