Deploy a monorepo
One repository, several deployable apps, each with its own file and its own lifecycle. Nothing is ever discovered — you list what deploys.
The layout
shop/
├── pagifier.toml ← root manifest: lists the projects
├── frontend/
│ ├── pagifier.toml
│ └── src/…
└── api/
├── pagifier.toml
└── src/…
version = 1
projects = [
"frontend/pagifier.toml",
"api/pagifier.toml",
]
The root manifest is the explicit inventory: a project not listed here does not exist as far as Pagifier is concerned.
The two projects
version = 1
name = "shop-frontend"
template = "vite-static"
environment = "prod"
ingress = "shop.company.com"
# Same-host path routing to the API service:
[[routes]]
path = "/api"
service = "shop-api"
port = 80
version = 1
name = "shop-api"
template = "nestjs"
environment = "prod"
ingress = "api.shop.company.com"
[secrets]
provider = "aws"
[secrets.keys]
DATABASE_URL = "prod/shop/database-url"
Each project is completely independent: its own releases, its own
rollbacks, its own canary progression. The frontend's routes block
proxies shop.company.com/api/* to the API service inside the cluster
— no CORS needed for same-host calls.
Deploying only what changed
Each project deploys with its own upload — so CI uses path filters:
name: deploy
on:
push:
branches: [main]
jobs:
changes:
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
api: ${{ steps.filter.outputs.api }}
steps:
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
frontend: ['frontend/**']
api: ['api/**']
frontend:
needs: changes
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: company/pagifier-deploy@v1
with:
config: frontend/pagifier.toml
workdir: frontend
environment: prod
token: ${{ secrets.PAGIFIER_TOKEN }}
api:
needs: changes
if: needs.changes.outputs.api == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: company/pagifier-deploy@v1
with:
config: api/pagifier.toml
workdir: api
environment: prod
token: ${{ secrets.PAGIFIER_TOKEN }}
A commit touching only api/ deploys only the API; the frontend's
running release is untouched.
Shared code?
Zip whatever the build needs. If both apps build from the repo root
(shared packages, workspaces), keep workdir: . and set each project's
build.workDir:
[build]
enabled = true
workDir = "frontend" # where the commands run
commands = ["npm ci", "npm run build -w frontend"]
Tips that save monorepo pain
- Independent environments too: deploy
apitostagingwhilefrontendridesprod— environment is per-upload, not per-repo. - Preview both per PR: run both deploy jobs with
environment: preview; each gets its own auto-generated URL. - Identical artifact = no rebuild: if a commit touches only
frontend/, an accidentalapiupload is nearly free — Pagifier fingerprints build inputs and reuses the existing image.