monorepo-strategy
GitHub提供多服务 Monorepo 应用的部署策略,涵盖检测、服务发现、依赖排序及构建上下文配置。
Trigger Scenarios
Install
npx skills add nixopus/nixopus --skill monorepo-strategy -g -y
SKILL.md
Frontmatter
{
"name": "monorepo-strategy",
"metadata": {
"version": "1.0"
},
"description": "Deploy multi-service monorepo applications — service discovery, dependency ordering, shared build contexts, selective deployment, and compose generation. Use when the repository contains multiple deployable services, apps, or packages."
}
Monorepo Deployment Strategy
Detection
A repository is a monorepo if any of these are present:
| Signal | Type |
|---|---|
apps/ or services/ directory with multiple subdirectories |
Conventional structure |
packages/ with shared libraries |
Shared code |
workspaces in root package.json |
npm/yarn/bun workspaces |
pnpm-workspace.yaml |
pnpm workspaces |
turbo.json |
Turborepo |
nx.json |
Nx |
lerna.json |
Lerna |
rush.json |
Rush |
go.work |
Go workspaces |
Multiple Dockerfile files in subdirectories |
Multi-service |
Multiple Cargo.toml with [workspace] in root |
Rust workspace |
Service Discovery
Node.js monorepos
- Read workspace configuration:
- npm/yarn/bun:
workspacesarray in rootpackage.json - pnpm:
packageslist inpnpm-workspace.yaml
- npm/yarn/bun:
- For each workspace package, read its
package.json - A package is a deployable service if it has a
startscript or amain/moduleentry - A package is a shared library if other packages depend on it but it has no start script
Go monorepos
- Read
go.workfor module list - Each module with a
mainpackage (main.goorcmd/directory) is deployable
Rust monorepos
- Read root
Cargo.toml→[workspace].members - Each member with
[[bin]]target orsrc/main.rsis deployable
Generic (no workspace tool)
- Scan
apps/,services/,packages/for subdirectories - Each subdirectory with its own manifest file (
package.json,go.mod,Cargo.toml, etc.) is a potential service - Each subdirectory with its own
Dockerfileis a deployable service
Dependency Graph
Build the service dependency graph before deploying:
- For each service, read its dependencies on other workspace packages
- Topologically sort: deploy dependencies before dependents
- Shared libraries are built first (they're dependencies of services)
Node.js dependency detection
In package.json, workspace dependencies use:
"@scope/package": "workspace:*"(pnpm)"@scope/package": "*"with the package inworkspaces(npm/yarn)
Go dependency detection
In each module's go.mod, require directives pointing to other workspace modules (matched by module path from go.work).
Build Context
The Docker build context for monorepo services should usually be the repository root, not the service subdirectory:
services:
api:
build:
context: . # repo root
dockerfile: apps/api/Dockerfile # service-specific Dockerfile
This ensures shared packages, root lockfiles, and workspace configuration are available during build.
Dockerfile for monorepo service
FROM node:22-slim AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/api/package.json ./apps/api/
COPY packages/shared/package.json ./packages/shared/
RUN corepack enable && pnpm install --frozen-lockfile
FROM deps AS build
COPY packages/shared ./packages/shared
COPY apps/api ./apps/api
RUN pnpm --filter api build
FROM node:22-slim
WORKDIR /app
COPY --from=build /app/apps/api/dist ./dist
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/apps/api/package.json ./
EXPOSE 3001
CMD ["node", "dist/index.js"]
Key: copy ALL workspace package.json files in the deps stage so the lockfile resolves correctly.
Selective Deployment
Not every push requires deploying every service:
- Determine which files changed (from deployment context or git diff)
- Map changed files to affected services:
apps/api/**→ deployapipackages/shared/**→ deploy ALL services that depend onshared- Root config (
package.json, lockfile,tsconfig.json) → deploy ALL services
- Only deploy affected services
Compose for Monorepos
Generate docker-compose.yml with one service per deployable app:
services:
api:
build:
context: .
dockerfile: apps/api/Dockerfile
ports:
- "3001:3001"
depends_on:
db:
condition: service_healthy
web:
build:
context: .
dockerfile: apps/web/Dockerfile
ports:
- "3000:3000"
depends_on:
- api
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
volumes:
pgdata:
Turborepo / Nx Considerations
If turbo.json or nx.json is present:
- Turbo: Use
turbo run build --filter=<service>for targeted builds - Nx: Use
nx build <service>ornx affected --target=buildfor selective builds - Both tools handle dependency ordering automatically
- In Docker: install the build orchestrator during the build stage, use it for targeted builds
Gotchas
- pnpm hoists differently than npm/yarn —
--shamefully-hoistmay be needed for some packages - Root
tsconfig.jsonwithreferencesmust be present for TypeScript project references to work - Turborepo
--filteruses package names frompackage.json, not directory names - Go workspaces:
go.work.summust also be committed alongsidego.work - Nx
affectedneeds git history in the Docker build — use--base=HEAD~1or copy.git(adds size) - Each service in a compose file can have different environment variables — don't share
.envacross services that need different configs
Related Skills
compose-setup— Base compose patterns for databases and cachesdockerfile-generation— Ecosystem-specific Dockerfile patterns to adapt for monorepo servicesnode-deploy— Node.js monorepo support (workspaces, package managers)go-deploy— Go workspace support
Version History
- cf05d97 Current 2026-08-20 14:52


