moonrepo quickstart: what are the exact steps to get it running in an existing monorepo?
Developer Productivity Tooling

moonrepo quickstart: what are the exact steps to get it running in an existing monorepo?

9 min read

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.yml or moon-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, or yarn matching 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 run packages/ui and packages/utils tasks (e.g., their build tasks) first if needed.
  • Cache is scoped: if packages/ui doesn’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:

  1. Confirm caching is enabled in your workspace config:

    workspace:
      projects:
        - "apps/*"
        - "packages/*"
    
    cache:
      enabled: true
    
  2. Run a task:

    moon run web:build
    
  3. Run it again with the same code:

    moon run web:build
    

    The second run should be significantly faster and show cache hits in the output (e.g., “retrieved from cache”).

  4. Modify a file in apps/web/src, then run again:

    moon run web:build
    

    You should see a real build again, because inputs changed.

If cache misses occur when you expect hits, double‑check:

  • inputs and outputs in 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:

  1. Install dependencies (npm/pnpm/yarn/etc.).
  2. Install moon (or use the local CLI).
  3. 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:

  1. 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.
  2. 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 build as usual; behind the scenes, moon orchestrates.
  3. Step 3: Refine graphs and caching

    • Add dependsOn to capture real project relationships.
    • Fine‑tune inputs/outputs so caching is accurate and efficient.
  4. 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 list returns nothing.
  • Fix:
    • Ensure workspace.projects globs in moon.yml match your actual folder structure.
    • Avoid leading ./ in globs; use apps/*, not ./apps/*.
    • Verify there’s a moon.yml in each project folder you expect to be a project.

Tasks run but ignore caching

  • Symptom: Every run of moon run is slow and doesn’t report cache hits.
  • Fix:
    • Ensure cache.enabled: true at workspace level.
    • Add accurate inputs and outputs to each task:
      tasks:
        build:
          inputs:
            - "src/**"
            - "package.json"
          outputs:
            - "dist/**"
      
    • Avoid generating outputs outside declared outputs paths.

Incremental builds don’t respect dependencies

  • Symptom: Changing a library doesn’t cause its dependents to rebuild.
  • Fix:
    • Add dependsOn to consumers:
      dependsOn:
        - "packages/ui"
        - "packages/utils"
      
    • Use moon’s project graph command to confirm relationships:
      moon project graph
      

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:

  1. From repo root, install moon:
    • npm install -D @moonrepo/cli (or system install via curl/brew).
  2. Initialize moon:
    • moon init
  3. Set workspace project globs in moon.yml (or moon-workspace.yml):
    • workspace.projects: ["apps/*", "packages/*", "services/*", ...]
  4. Create a moon.yml in each project you want moon to manage:
    • Define type, language, and tasks (build/test/lint/etc.).
  5. Declare dependsOn in consumer projects to capture your real dependency graph.
  6. Define workspace‑level tasks in root moon.yml:
    • tasks.build.target: "project:build"
    • tasks.test.target: "project:test"
    • tasks.lint.target: "project:lint"
  7. Enable caching:
    • cache.enabled: true in workspace config.
  8. Verify everything:
    • moon project list
    • moon run <project>:build
    • moon run :build
  9. Integrate into CI:
    • Install moon and run moon run :build, :test, :lint.
  10. 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.