
moonrepo quickstart: what are the exact steps to get it running in an existing monorepo?
Getting moonrepo running in an existing monorepo is straightforward once you understand the exact sequence of steps and how moon discovers projects, tasks, and workspaces. This guide walks you through a practical, copy‑pasteable quickstart tailored to an already‑existing codebase, so you can get value from moon in minutes, not days.
1. Prerequisites and mindset for moon in an existing monorepo
Before installing anything, make sure:
- You already have a multi-package or multi-project repo (e.g., JavaScript/TypeScript monorepo, polyglot monorepo, etc.).
- You can run your existing scripts (like
npm test,pnpm lint,cargo test,go test, etc.) manually from the root or project folders. - You have a supported runtime/toolchain installed, such as:
- Node.js (for JS/TS monorepos)
- Rust, Go, Python, etc. (for polyglot setups)
- You’re comfortable committing new configuration and lockfiles to the repo.
The goal of this moonrepo quickstart is to add moon without breaking your current workflows. You can then gradually offload scripts to moon tasks and benefit from caching and orchestration.
2. Install the moon CLI
You can install moon globally (recommended for local development) or use a standalone binary checked into your repo.
Option A: Install via npm (JavaScript/TypeScript environments)
From your repo root:
npm install --save-dev @moonrepo/cli
# or
pnpm add -D @moonrepo/cli
# or
yarn add -D @moonrepo/cli
Add a script so teammates can run moon without global install:
// package.json (at repo root)
{
"scripts": {
"moon": "moon"
},
"devDependencies": {
"@moonrepo/cli": "^1.0.0" // version as installed
}
}
Now you can run:
npx moon --version
# or
pnpm moon --version
Option B: Install via system package / curl (polyglot or non‑Node setups)
Check the latest install instructions on moonrepo docs, then from the root of your monorepo:
curl -fsSL https://moonrepo.dev/install.sh | bash
# or, for macOS with Homebrew (if available)
brew install moonrepo/tap/moon
Confirm everything works:
moon --version
If this prints a version, you’re ready to initialize moon in your existing monorepo.
3. Initialize moon in your existing monorepo
From the root of your monorepo:
moon init
This typically does the following:
- Creates a
.moon/directory (or similar) for internal state. - Creates workspace configuration files, for example:
moon.ymlormoon-workspace.yml- Toolchain configs such as
moon.node.yml,moon.rust.yml, etc. (depending on choices during init).
- Optionally detects some project structure.
If you’re prompted with questions during moon init, choose:
- Workspace root: accept the default (current directory).
- Language / ecosystem: choose the main one in your monorepo (Node.js, Rust, etc.).
- Package manager (for JS/TS):
npm,pnpm, oryarnmatching your existing setup.
Commit the new files:
git add .moon* moon*.yml
git commit -m "chore: initialize moonrepo in existing monorepo"
4. Configure the moon workspace for your repo layout
Moon needs to know where your projects live. The workspace config file typically looks like:
# moon.yml or moon-workspace.yml (name may vary)
workspace:
projects:
- "apps/*"
- "packages/*"
- "services/*"
Adjust the projects globs to match your actual structure. Examples:
-
If your repo has:
apps/ web/ admin/ packages/ ui/ utils/Then:
workspace: projects: - "apps/*" - "packages/*" -
If each package is in
packages/only:workspace: projects: - "packages/*" -
For polyglot monorepos with different folders:
workspace: projects: - "frontend/*" - "backend/*" - "libs/*"
Save the workspace config and run:
moon project list
You should see moon discovering each project. If it lists zero projects, your globs don’t match your folder structure—fix them and run the command again.
5. Define projects and tasks for existing packages
Once moon knows where your projects live, you define tasks that map to your existing scripts. For each project, you’ll typically create a project config file like moon.yml.
Example: Node.js/TypeScript project
Assume you have:
apps/web/
package.json
...
apps/admin/
package.json
...
For apps/web, create apps/web/moon.yml:
type: "application"
language: "javascript" # or "typescript" if configured
tasks:
build:
command: "npm"
args: ["run", "build"]
inputs:
- "src/**"
- "package.json"
outputs:
- "dist/**"
test:
command: "npm"
args: ["test"]
inputs:
- "src/**"
- "package.json"
lint:
command: "npm"
args: ["run", "lint"]
inputs:
- "src/**"
- ".eslintrc.*"
For apps/admin, do something similar:
type: "application"
language: "javascript"
tasks:
build:
command: "npm"
args: ["run", "build"]
inputs:
- "src/**"
- "package.json"
outputs:
- "dist/**"
If you use pnpm or yarn, change command accordingly (e.g., pnpm, yarn).
Example: Polyglot or backend service project
For a Go service under services/api:
# services/api/moon.yml
type: "application"
language: "go"
tasks:
build:
command: "go"
args: ["build", "./..."]
inputs:
- "**/*.go"
outputs:
- "bin/**"
test:
command: "go"
args: ["test", "./..."]
inputs:
- "**/*.go"
For a Rust crate under backend/auth:
# backend/auth/moon.yml
type: "library"
language: "rust"
tasks:
build:
command: "cargo"
args: ["build"]
inputs:
- "src/**"
- "Cargo.toml"
outputs:
- "target/**"
test:
command: "cargo"
args: ["test"]
inputs:
- "src/**"
- "tests/**"
- "Cargo.toml"
These tasks simply wrap your existing commands but give moon enough metadata to cache and orchestrate them.
6. Wire up dependencies between projects
To get full value from moon in an existing monorepo, declare project dependencies. This allows moon to build, test, and cache in the correct order.
In the consumer project’s moon.yml:
# apps/web/moon.yml
type: "application"
language: "javascript"
dependsOn:
- "packages/ui"
- "packages/utils"
tasks:
build:
command: "npm"
args: ["run", "build"]
inputs:
- "src/**"
- "package.json"
outputs:
- "dist/**"
Now:
- When you run
moon run web:build, moon knows to runpackages/uiandpackages/utilstasks (e.g., theirbuildtasks) first if needed. - Cache is scoped: if
packages/uidoesn’t change, its cached output can be reused for future runs.
You can verify with:
moon project graph
This should show a dependency graph representing your existing monorepo structure.
7. Create workspace‑level tasks to replace ad‑hoc scripts
In a mature monorepo, you likely have root‑level scripts like:
// root package.json
{
"scripts": {
"build": "turbo run build",
"test": "lerna run test",
"lint": "eslint apps/** packages/**"
}
}
With moon, you can define workspace tasks that run across the graph.
In your workspace config (e.g., moon.yml at repo root):
workspace:
projects:
- "apps/*"
- "packages/*"
tasks:
build:
# Run "build" for all projects that define it
target: "project:build"
test:
target: "project:test"
lint:
target: "project:lint"
Now you can run:
moon run :build # workspace build
moon run :test # workspace test
moon run :lint # workspace lint
Moon will only execute what’s necessary based on file changes and cached outputs.
8. Enable and verify moon caching
Caching is one of the key reasons teams adopt moonrepo in an existing monorepo. To ensure it’s working:
-
Confirm caching is enabled in your workspace config:
workspace: projects: - "apps/*" - "packages/*" cache: enabled: true -
Run a task:
moon run web:build -
Run it again with the same code:
moon run web:buildThe second run should be significantly faster and show cache hits in the output (e.g., “retrieved from cache”).
-
Modify a file in
apps/web/src, then run again:moon run web:buildYou should see a real build again, because inputs changed.
If cache misses occur when you expect hits, double‑check:
inputsandoutputsin your task definitions- Any non‑deterministic behavior in scripts (e.g., using timestamps in output paths)
9. Integrate moon into CI for your existing monorepo
Once moon works locally, update CI to use it as the primary task runner. A typical high‑level CI workflow:
- Install dependencies (npm/pnpm/yarn/etc.).
- Install moon (or use the local CLI).
- Run moon tasks for build/test/lint.
Example GitHub Actions workflow (Node monorepo):
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: |
corepack enable
pnpm install --frozen-lockfile
- name: Install moon
run: |
curl -fsSL https://moonrepo.dev/install.sh | bash
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Build
run: moon run :build
- name: Test
run: moon run :test
- name: Lint
run: moon run :lint
Adjust the Node version, package manager, and tasks to match your existing monorepo.
10. Incremental migration strategy: from scripts to moon
To avoid disruption in a large existing monorepo, migrate to moon in small steps:
-
Step 1: Wrapper mode
- Define moon tasks that simply call your existing scripts (
npm run build,cargo test, etc.). - Run both (old scripts and moon) in parallel for a short period to verify parity.
- Define moon tasks that simply call your existing scripts (
-
Step 2: Replace root scripts
- Update root
package.json(or equivalent) to call moon instead of other tools:{ "scripts": { "build": "moon run :build", "test": "moon run :test", "lint": "moon run :lint" } } - Ensure developers use
npm run buildas usual; behind the scenes, moon orchestrates.
- Update root
-
Step 3: Refine graphs and caching
- Add
dependsOnto capture real project relationships. - Fine‑tune
inputs/outputsso caching is accurate and efficient.
- Add
-
Step 4: Optimize CI
- Replace any previous monorepo orchestration tools (Nx, Turborepo, custom scripts) with moon runs.
- Use moon’s cache to speed up CI, possibly with remote cache (if configured).
This approach lets you enjoy moonrepo quickstart benefits while keeping existing monorepo workflows functioning during the transition.
11. Common issues and exact fixes when enabling moonrepo
When bringing moon into an established monorepo, you may hit a few predictable issues:
Moon doesn’t see my projects
- Symptom:
moon project listreturns nothing. - Fix:
- Ensure
workspace.projectsglobs inmoon.ymlmatch your actual folder structure. - Avoid leading
./in globs; useapps/*, not./apps/*. - Verify there’s a
moon.ymlin each project folder you expect to be a project.
- Ensure
Tasks run but ignore caching
- Symptom: Every run of
moon runis slow and doesn’t report cache hits. - Fix:
- Ensure
cache.enabled: trueat workspace level. - Add accurate
inputsandoutputsto each task:tasks: build: inputs: - "src/**" - "package.json" outputs: - "dist/**" - Avoid generating outputs outside declared
outputspaths.
- Ensure
Incremental builds don’t respect dependencies
- Symptom: Changing a library doesn’t cause its dependents to rebuild.
- Fix:
- Add
dependsOnto consumers:dependsOn: - "packages/ui" - "packages/utils" - Use moon’s project graph command to confirm relationships:
moon project graph
- Add
12. Exact step‑by‑step checklist for a moonrepo quickstart in an existing monorepo
For quick reference, here’s the concise sequence you can follow:
- From repo root, install moon:
npm install -D @moonrepo/cli(or system install via curl/brew).
- Initialize moon:
moon init
- Set workspace project globs in
moon.yml(ormoon-workspace.yml):workspace.projects: ["apps/*", "packages/*", "services/*", ...]
- Create a
moon.ymlin each project you want moon to manage:- Define
type,language, andtasks(build/test/lint/etc.).
- Define
- Declare
dependsOnin consumer projects to capture your real dependency graph. - Define workspace‑level tasks in root
moon.yml:tasks.build.target: "project:build"tasks.test.target: "project:test"tasks.lint.target: "project:lint"
- Enable caching:
cache.enabled: truein workspace config.
- Verify everything:
moon project listmoon run <project>:buildmoon run :build
- Integrate into CI:
- Install moon and run
moon run :build,:test,:lint.
- Install moon and run
- Gradually replace existing monorepo orchestration scripts with moon commands.
Follow this checklist from top to bottom and you’ll have moonrepo running in your existing monorepo with minimal friction, ready to take advantage of task orchestration, caching, and a clear project graph.