Back to Blog
Tutorial12 min read2026-08-02

Deploying a Monorepo Subdirectory Without Building the Entire Tree

Point PandaStack at apps/web in your monorepo and deploy just that service. The rootDir field in pandastack.json makes the build context shallow, skipping unrelated packages.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

A monorepo holds multiple services in one Git repository: apps/web, apps/api, apps/admin, each with its own package.json and build process. Deploying one of them shouldn't require installing dependencies for the other three, but most platforms clone the entire repository and run npm install at the root, which wastes time and storage.

PandaStack supports a rootDir field that changes the build context to a subdirectory. The build process cds into apps/web, runs npm install there, and ignores the rest of the monorepo. This works for npm workspaces, Yarn workspaces, pnpm, Turborepo, and Nx — any structure where services are self-contained.

Monorepo structure

Your repository might look like this:

repo/
  package.json        (root workspace config)
  apps/
    web/
      package.json
      src/
      vite.config.js
    api/
      package.json
      src/
  packages/
    shared-ui/
      package.json

The apps/web directory is a standalone Vite app. It depends on packages/shared-ui, but PandaStack doesn't need to know that — npm/yarn/pnpm handle workspace dependencies automatically when you run npm install inside apps/web.

Configure pandastack.json for the subdirectory

Create apps/web/pandastack.json:

{
  "type": "static",
  "language": "nodejs",
  "buildCommand": "npm run build",
  "outputDir": "dist",
  "rootDir": "apps/web"
}

The rootDir field tells PandaStack: clone the full repository, then cd into apps/web before running build commands. The outputDir is now relative to apps/web, not the repository root, so it resolves to apps/web/dist.

Deploy via the REST API

Create a project that deploys only the apps/web subdirectory:

curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer $PANDASTACK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "static",
    "name": "monorepo-web",
    "repositoryName": "acme/monorepo",
    "branch": "main",
    "autoDeploy": true,
    "rootDir": "apps/web"
  }'

The API field is rootDir, matching the JSON schema. If you're using the deploy button, the query parameter is rootDir or root-directory (both work).

What happens during the build

PandaStack clones the full repository, then runs:

cd apps/web
npm install
npm run build

If apps/web/package.json references a workspace dependency like @repo/shared-ui, npm resolves it by traversing up to the root package.json and following the workspace configuration. The build gets the shared code without you having to configure PandaStack to install root dependencies first.

Deploy button for a subdirectory

If this is a starter template or open-source project, add a deploy button to apps/web/README.md:

[![Deploy to PandaStack](https://dashboard.pandastack.io/deploy-button.svg)](https://dashboard.pandastack.io/deploy?repo=acme/monorepo&rootDir=apps/web)

The rootDir query parameter seeds the deploy form, and when PandaStack fetches pandastack.json, it reads it from apps/web/pandastack.json (not the repository root) because that's the effective root directory.

Use the CLI

panda login
panda projects create \
  --name monorepo-web \
  --repo acme/monorepo \
  --branch main \
  --type static \
  --auto-deploy

The CLI doesn't have a --root-dir flag yet, so you need to add rootDir via the dashboard (Settings → General) or include it in pandastack.json.

When workspace dependencies break

If your build fails with Cannot find module '@repo/shared-ui', the workspace resolution isn't working. This usually means one of three things:

  1. 1The root package.json is missing the workspaces field. pnpm reads pnpm-workspace.yaml instead, so make sure that file exists.
  1. 1The shared package isn't installed. Some monorepo setups require running npm install at the root first, then again in the subdirectory. PandaStack only runs it in the subdirectory. Fix this by adding a postinstall script to apps/web/package.json:

`json

{

"scripts": {

"postinstall": "cd ../.. && npm install"

}

}

`

  1. 1The package manager is different locally vs in the build. If you use pnpm locally but PandaStack detects npm (because there's a package-lock.json), delete the lockfile and commit pnpm-lock.yaml instead.

Deploy multiple services from the same repo

Create separate projects for apps/web and apps/api:

# Deploy the frontend
curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer $PANDASTACK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "static",
    "name": "monorepo-web",
    "repositoryName": "acme/monorepo",
    "branch": "main",
    "rootDir": "apps/web"
  }'

# Deploy the backend
curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer $PANDASTACK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "container",
    "name": "monorepo-api",
    "repositoryName": "acme/monorepo",
    "branch": "main",
    "rootDir": "apps/api"
  }'

Both projects share the same Git repository but deploy independently. Pushing to main triggers rebuilds for both.

Turborepo and build caching

If you use Turborepo, the turbo.json at the repository root declares build dependencies. When you deploy apps/web, Turborepo knows it depends on packages/shared-ui and builds both in the correct order.

Make sure your buildCommand invokes Turborepo:

{
  "buildCommand": "npx turbo run build --filter=web",
  "rootDir": "apps/web"
}

The --filter=web flag builds only the web app and its dependencies, skipping unrelated services.

Debugging build failures

npm ERR! 404 Not Found - GET https://registry.npmjs.org/@repo%2fshared-ui: The workspace package name is being treated as an npm registry package. Check that the root package.json has "workspaces": ["apps/*", "packages/*"].

Error: Output directory 'dist' not found: The outputDir is relative to rootDir, so "outputDir": "apps/web/dist" is wrong. Use "outputDir": "dist" instead.

Build runs at the repository root instead of the subdirectory: You set rootDir in pandastack.json but forgot to push the file. Verify it's committed to the branch you're deploying.

Performance: does cloning the full repo waste time?

Git clones are fast because they transfer only changed objects. If your monorepo is 500 MB but you push a 10 KB change, the incremental clone takes seconds. The savings from rootDir come from skipping npm install and npm run build for unrelated services, not from avoiding the clone.

If clone times are genuinely a problem (e.g., your repo includes gigabytes of binary assets), use Git LFS or split the monorepo into separate repositories.

References

  • [npm workspaces](https://docs.npmjs.com/cli/v10/using-npm/workspaces)
  • [pnpm workspaces](https://pnpm.io/workspaces)
  • [Turborepo filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering)
  • [PandaStack monorepo guide](https://docs.pandastack.io/projects)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also