Monorepos
Paas supports both shapes:
- One repo per app — the default.
git pushto that repo deploys that one app. - Monorepo — one git repo holds many apps. A push fans out to only the apps whose code (or watched paths) actually changed.
The model is the same in both cases: each App points to a Repository and
declares a BuildContext (a subdirectory) plus optional WatchPaths
(glob patterns). The default for a single-app repo is BuildContext='.' —
the whole repo is the build context.
Setting up a monorepo
1. Create the Repository
Dashboard → project → Repositories card → Create repo.
Slug = mono (or whatever you like). The dashboard returns a clone URL:
git@git.example.com:acme/storefront/mono.git
2. Create one App per service
For each service in the monorepo, create an App and select Use existing repo: mono. Set its Build context to the subdirectory that holds the service code (relative to repo root). Optionally add Watch paths for shared code:
| App | Build context | Watch paths |
|---|---|---|
| frontend | apps/web |
packages/ui/** |
| backend | services/api |
packages/db/** |
| worker | workers/jobs |
packages/db/** |
3. Push once
git remote add paas ssh://git@git.example.com:2222/acme/storefront/mono.git
git push paas main
The git server's post-receive hook computes git diff --name-only OLD..NEW
and sends the file list to the API. The API:
- Picks Apps in this repo whose
BuildContext(orWatchPaths) match any of the changed files. - For each matching App, picks Environments whose
BranchPatternmatches the pushed branch. - Queues a deployment per (App × Env) pair.
The git client sees one line per queued deploy:
[paas] push branch=main sha=a1b2c3d files=12
[paas] queued: app=frontend env=production deployment=5869…
[paas] queued: app=worker env=production deployment=fc81…
If nothing matches, you get a friendly skip:
[paas] skipped: no app's BuildContext/WatchPaths matches the 3 changed file(s)
Path matching rules
For each App in the pushed repo:
- If
BuildContext == "."→ always redeploy (whole-repo project). - Else if any changed file starts with
BuildContext + "/"→ redeploy. - Else if any changed file matches one of
WatchPaths(glob) → redeploy. - Else skip.
Glob syntax
| Pattern | Matches |
|---|---|
* |
any chars except / |
** |
any chars including / (any depth) |
? |
a single non-slash char |
packages/ui/** |
every file under packages/ui/ |
*.proto |
any .proto at the repo root |
**/migrations/** |
any path with a migrations/ segment |
Anchored to the repo root. Patterns are normalized to forward slashes.
How the build differs
Builder runs:
docker build -t <registry>/<org>/<project>/<app>:<sha> \
-f <BuildContext>/<DockerfilePath> \
<BuildContext>
So the docker context only contains the App's subdirectory. If a service
needs files from outside its BuildContext at build time (e.g. a shared
proto file in packages/proto/), use a multi-stage build that copies them
explicitly via a build context that includes them — typically by setting
BuildContext='.' and selecting the Dockerfile via DockerfilePath:
BuildContext = .
DockerfilePath = apps/web/Dockerfile
WatchPaths = ["apps/web/**", "packages/**"]
This is the most flexible setup for monorepos that share code; the trade-off is the docker build sees the whole repo as context, which is bigger and slower.
Editing branches per environment
Each environment has a BranchPattern that decides what triggers a deploy.
You can change it for any environment, including the auto-created
Production. Dashboard → project → Environments card → click Edit
next to the pattern.
Common setups:
| Project shape | Production pattern | Staging pattern | Preview pattern |
|---|---|---|---|
| Trunk-based | main |
(not used) | (not used) |
| GitFlow | release |
develop |
* |
| GitHub Flow + previews | main |
staging |
* |
Specific patterns (main, staging) win over the catch-all *. If you
push to a branch matching only *, only environments using * deploy. If
no env matches at all, the push is accepted but no deploys are queued — the
git client sees [paas] skipped: no environment matches branch '...'.
CLI cheat-sheet
# Create a repo at the project level
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"slug":"mono"}' \
https://paas.example.com/api/orgs/acme/projects/storefront/repos/
# Create an app pointing at a subdir of an existing repo
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"slug":"frontend",
"displayName":"Frontend",
"containerPort":3000,
"repoSlug":"mono",
"buildContext":"apps/web",
"dockerfilePath":"Dockerfile",
"watchPaths":["packages/ui/**"]
}' \
https://paas.example.com/api/orgs/acme/projects/storefront/apps/
# Update an existing app's build paths
curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"buildContext":"apps/web","watchPaths":["packages/ui/**","packages/db/**"]}' \
https://paas.example.com/api/orgs/acme/projects/storefront/apps/frontend
# Change Production's branch from `main` to `release`
curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"branchPattern":"release"}' \
https://paas.example.com/api/orgs/acme/projects/storefront/envs/production