---
id: platform/monorepos
title: Monorepo builds
description: Deploy separate services from one repository with explicit root directories, Docker contexts, and automatic-deployment filters.
keywords: [bex, monorepo, rootDir, dockerContext, build filters]
last_updated: 2026-09-23
---

Deploy each independently operated application in a repository as a separate
Bex service. Give it its own build/start commands, configuration, and deployment
policy. A shared repository does not make services share environment variables
or scale together.

## Runnable shared-package sample

```bash
curl -fsSLO https://bex.co/examples/shared-package-monorepo-latest.zip
curl -fsSLO https://bex.co/examples/shared-package-monorepo-latest.zip.sha256
shasum -a 256 -c shared-package-monorepo-latest.zip.sha256
unzip shared-package-monorepo-latest.zip
cd shared-package-monorepo
npm install
npm run start:api
npm run start:worker-api
npm test
npm run rehearse
```

See also [`shared-package-monorepo` README](/examples/shared-package-monorepo/README.md)
for Docker/Blueprint details. The archive includes a complete two-service Node
workspace with a shared package, root lockfile, Dockerfiles built from the
repository root, and a Blueprint whose filters include `packages/shared/**` and
`package-lock.json` while ignoring `docs/**`.

Each service returns its own `service` identity plus `sharedMessage` /
`sharedVersion` from the workspace package. `npm run rehearse` prints an
**explanatory** push-deploy matrix checked against upstream filter composition
(Bex `d0f17feb3ece1ce20c8c85384e8ee68ff4b2e1d3`); it is not live hosted evidence.
Push the same scenarios from a repository you own after connecting Git and
applying the Blueprint — inspect per-service deploy history and `/` responses.

## Choose the service root

`rootDir` selects the directory used for a service's build. Omit it to use the
repository root. A self-contained Node.js app under `apps/api` can use:

```yaml
services:
  - type: web
    name: api
    runtime: node
    repo: https://github.com/acme/monorepo
    rootDir: apps/api
    buildCommand: npm ci
    startCommand: npm start
    autoDeployTrigger: commit
    buildFilter:
      paths:
        - apps/api/**
      ignoredPaths:
        - apps/api/docs/**
```

This assumes `apps/api` contains its own package manifest and lockfile, and its
start command listens on `0.0.0.0:$PORT`. Replace the example repository and
commands. Validate using the [Blueprint guide](./app-resource.md) before applying.

Native commands execute in the built application's working directory. Adjust
build/start commands and static publish paths when changing the root. A package
that relies on a root-level workspace lockfile or sibling package may need to
build from the repository root instead, using package-manager workspace commands.
Do not solve missing files with `../` traversal in a configured root path.

## Dockerfile and build context

Bex resolves the Dockerfile relative to `rootDir`. By default, the Docker build
context is also that directory. An explicit `dockerContext` selects a context
relative to the **repository root**, independently of `rootDir`.

For shared source, keeping the service root at the repository root makes the
relationship clear:

```yaml
services:
  - type: web
    name: api
    runtime: docker
    repo: https://github.com/acme/monorepo
    dockerfilePath: apps/api/Dockerfile
    dockerContext: .
    autoDeployTrigger: commit
    buildFilter:
      paths:
        - apps/api/**
        - packages/shared/**
        - package-lock.json
```

Write Dockerfile `COPY` paths relative to that build context, and check
`.dockerignore`. A path to the Dockerfile does not make its containing directory
the context. See [Docker deployments](./docker-deploys.md) for registry and image
requirements.

## Decide which pushes deploy

Bex checks the tracked repository/branch, auto-deploy setting, changed files
under `rootDir`, and `buildFilter`. Build-filter globs are repository-relative:

- With included `paths`, a changed file must match one of them.
- An `ignoredPaths` match excludes that file even if an include also matches.
- With no included paths, changes are eligible unless ignored.

**Root-directory and build-filter checks both apply.** Adding a sibling package
to a filter does not bypass a narrow `rootDir` gate. If shared-code changes must
redeploy a service, use a root and filter combination that admits them, such as
the repository-root Docker example above. Include relevant lockfiles and shared
configuration as well as application source.

If a webhook carries no changed-path information, Bex currently allows the
path-filter checks to pass; filters are not a strict guarantee against deployment.

Filters control push-triggered deployments, not access to files or authorization.
An explicit manual deploy is a separate operation. Do not use filters to enforce
a release approval policy; Bex does not implement `checksPass` CI gating.

## Verify and troubleshoot

Test an application change, an ignored documentation change, and a shared-library
change in a non-production branch/workspace. Check which service deploys and
which commit it builds.

For a missing file, inspect the root, context, Dockerfile path, lockfile placement,
and ignore rules. For a missing deployment, inspect branch and auto-deploy
settings, webhook delivery, root matching, and filters. For an unexpected
service restart, also check [Blueprint sync](./blueprints.md) and configuration
changes. [Build logs](./logging.md) distinguish a source-path failure from an
application startup failure.

## Related

- [Shared-package monorepo sample](/examples/shared-package-monorepo/README.md)
- [Docker deployments](./docker-deploys.md)
- [GitHub integration](./github-integration.md)
- [Blueprints](./blueprints.md)
- [Health checks](./health-checks.md)
