Moon: Task Runner and Build System for Monorepos
Managing a monorepo with multiple packages requires a build system that understands dependencies between them, runs only what changed, and caches results. Moon is a task runner and build system that provides dependency graphs, change detection, remote caching, and toolchain management — without being tied to a specific package manager or language.
Install
# Install moon globally
curl -fsSL https://moonrepo.dev/install/moon.sh | bash
# or
npm install --save-dev @moonrepo/cli
Workspace Setup
Initialize moon in a monorepo:
moon init
This creates:
.moon/
workspace.yml # Workspace configuration
toolchain.yml # Toolchain versions
tasks.yml # Inherited tasks
workspace.yml
# .moon/workspace.yml
$schema: "https://moonrepo.dev/schemas/workspace.json"
projects:
# Explicitly list projects
- web
- api
- packages/ui
- packages/utils
# Or use a glob
# projects:
# - "apps/*"
# - "packages/*"
vcs:
defaultBranch: main
manager: git
toolchain.yml
Moon manages language/tool versions for consistency across the team:
# .moon/toolchain.yml
$schema: "https://moonrepo.dev/schemas/toolchain.json"
node:
version: "22.0.0"
packageManager: pnpm
addEnginesConstraint: true
typescript:
version: "5.4.0"
When developers run moon commands, it installs and uses the exact toolchain versions — no more "works on my machine" from different Node versions.
Project Configuration
Each project has a moon.yml:
# web/moon.yml
$schema: "https://moonrepo.dev/schemas/project.json"
language: typescript
type: application
# Tags for grouping
tags:
- frontend
- react
# Project dependencies
dependsOn:
- packages/ui
- packages/utils
tasks:
dev:
command: vite dev
local: true # Don't run in CI
build:
command: vite build
inputs:
- "src/**/*"
- "public/**/*"
outputs:
- "dist"
deps:
- packages/ui:build
- packages/utils:build
test:
command: vitest run
inputs:
- "src/**/*.test.*"
- "src/**/*"
Running Tasks
# Run a task in a specific project
moon run web:build
# Run a task across all projects
moon run :build
# Run multiple tasks
moon run web:build api:build
# Run for a project and its dependencies
moon run web:build --dependents
Affected Detection
Moon tracks which files changed and runs only affected projects:
# Run build only for changed projects (since main branch)
moon run :build --affected
# Against a specific branch
moon run :test --affected --base main --head HEAD
This is the key CI optimization — a PR changing only packages/utils triggers builds only for utils and projects that depend on it.
Task Inheritance
Define tasks once in .moon/tasks.yml and inherit them across projects:
# .moon/tasks.yml
tasks:
format:
command: biome format --write .
inputs:
- "@globs(sources)"
lint:
command: biome lint .
inputs:
- "@globs(sources)"
typecheck:
command: tsc --noEmit
inputs:
- "@globs(sources)"
- "tsconfig.json"
Any project inherits these tasks unless it overrides them in its own moon.yml.
Dependency Graph
Visualize the dependency graph:
moon graph
moon graph web # Just one project's graph
Opens an interactive graph in your browser showing which projects depend on which.
Remote Caching
Moon supports remote caching to share build artifacts across CI jobs and machines:
# .moon/workspace.yml
runner:
cacheLifetime: "7 days"
inheritColorsForPipedTasks: true
With moonbase (Moon's cloud cache) or self-hosted S3-compatible storage:
runner:
archivableTargets:
- ":build"
- ":typecheck"
Once cached, identical tasks across CI runs use the cache without recomputing.
Proto: Toolchain Manager
Moon's companion tool proto manages multiple language versions:
# Install proto
curl -fsSL https://moonrepo.dev/install/proto.sh | bash
# Use .prototools file in repo root
node = "22.0.0"
pnpm = "9.0.0"
bun = "1.1.0"
# proto installs and activates the right version automatically
proto use
Proto replaces nvm, fnm, and similar tools with a unified manager for Node, Bun, Deno, Python, Go, and more.
Moon vs Turborepo
| Feature | Moon | Turborepo |
|---|---|---|
| Language agnostic | Yes | Node-focused |
| Toolchain management | Built-in (proto) | No |
| Remote caching | moonbase / self-hosted | Vercel (free tier) |
| Configuration | YAML | JSON |
| Project detection | Manual + glob | package.json-based |
| Docker integration | moon docker scaffold | Manual |
| Affected detection | Git-aware | Basic |
Moon is better for polyglot repos or when you want more control. Turborepo is simpler for pure Node/TypeScript monorepos already using npm/pnpm workspaces.
CI Integration
# .github/workflows/ci.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for affected detection
- uses: moonrepo/setup-moon-action@v1
- name: Run affected tasks
run: |
moon run :build --affected --base ${{ github.base_ref }}
moon run :test --affected --base ${{ github.base_ref }}
Docker Support
Moon generates optimized Dockerfiles for projects:
moon docker scaffold web
moon docker build web
This creates a Docker build that only copies relevant workspace files for the target project, using layer caching effectively.
Moon's documentation is at moonrepo.dev. For monorepos with multiple languages or projects, or where Turborepo's Node-centric model is limiting, Moon provides a more flexible and feature-complete alternative.