moonrepo: what config files should we commit to standardize tasks and conventions across teams?
Developer Productivity Tooling

moonrepo: what config files should we commit to standardize tasks and conventions across teams?

7 min read

Standardizing tasks and conventions across teams in a monorepo is exactly what Moonrepo is designed to help with—but only if the right configuration files are committed and shared. The core idea is simple: anything that defines how tasks run, how projects are structured, and how your repo behaves in CI should be versioned and visible to everyone.

Below is a practical, opinionated guide to which Moonrepo config files you should commit, how they work together, and how to structure them so every team in your organization benefits.


Core principle: commit all behavior, ignore only machines

When deciding what to commit, use this rule of thumb:

  • Commit: Anything that affects developer workflows, tasks, conventions, or CI behavior.
  • Ignore: Machine-specific caches, build outputs, logs, and local overrides.

Moonrepo’s configuration is designed to be team-wide and deterministic, so almost all of its config belongs in your repo.


Essential Moonrepo config files to commit

These are the baseline files you should commit in nearly every Moonrepo setup.

1. moon.yml (root workspace configuration)

Purpose: Defines global behavior for the entire workspace.

Why commit it:
This is the central source of truth for how your monorepo behaves—task runner behavior, language settings, conventions, and project discovery. Without a shared moon.yml, every developer (and CI) could behave differently.

Typical responsibilities:

  • Workspace metadata (name, version, owner).
  • Global task runner options (concurrency, caching, logging).
  • Language toolchain integration (Node, Rust, Go, etc.).
  • Project discovery and naming patterns.
  • Global tags, scopes, or conventions.

Example:

# moon.yml
workspace:
  name: "my-org"
  projects:
    globs:
      - "apps/*"
      - "packages/*"
    alias:
      client: "apps/web"
      api: "apps/api"

runner:
  logLevel: "info"
  concurrency: 8
  cache:
    enabled: true
    engine: "local"

node:
  version: "20.10.0"
  packageManager: "pnpm"
  pnpm:
    version: "9.0.0"

Standardization impact:

  • Every team uses the same Node version and package manager.
  • Projects follow the same layout and naming conventions.
  • Tasks run with consistent logging and caching behavior.

2. toolchain.yml (if used as a dedicated toolchain config)

Some teams prefer to separate language/tooling configuration from general workspace config.

Why commit it:

  • Ensures all teams use the same versions of Node, Rust, etc.
  • Guarantees consistent builds across local and CI environments.

What it typically includes:

  • Node, Rust, Go, Python versions.
  • Tool-specific settings (e.g., Rust targets, Node package manager config).

Example:

# toolchain.yml
node:
  version: "20.10.0"
  packageManager: "pnpm"
  pnpm:
    version: "9.0.0"

rust:
  version: "1.75.0"
  targets:
    - "x86_64-unknown-linux-gnu"

If you don’t use a separate toolchain.yml, you’ll usually put this directly in moon.yml.


3. Project-level configs: moon.yml or project.yml in each package/app

Purpose: Define tasks, dependencies, and conventions for individual projects in the repo.

Why commit them:

  • They define how each project is built, tested, linted, and deployed.
  • They encode task names and conventions that are shared across teams and CI.
  • They help ensure every project exposes the same common tasks (e.g., lint, test, build).

Example:

# apps/web/moon.yml
project:
  name: "web"
  type: "application"
  language: "javascript"
  tags: ["frontend"]

tasks:
  lint:
    command: "pnpm lint"
    inputs:
      - "src/**/*"
      - "package.json"
    outputs: []
    env:
      NODE_ENV: "development"

  test:
    command: "pnpm test"
    inputs:
      - "src/**/*"
      - "tests/**/*"
    outputs: []

  build:
    command: "pnpm build"
    inputs:
      - "src/**/*"
      - "package.json"
    outputs:
      - "dist"

Standardization patterns:

  • Every project defines lint, test, build with the same semantics.
  • Teams can rely on moon run web:build or moon run :test across the entire repo.
  • CI pipelines can be simplified to generic Moon tasks rather than project-specific scripts.

4. task-pipelines and shared task templates (if you use them)

In larger organizations, you may want to centralize common task definitions.

Where they live:

  • A shared directory (e.g. config/tasks/).
  • Sometimes referenced via includes or templates.

Why commit them:

  • Establish a single definition of what “lint” or “test” means for all projects.
  • Make it easy for new projects to adopt org-wide standards by referencing shared configs.

Example concept (pseudo-structure):

# config/tasks/frontend.yml
tasks:
  lint:
    command: "pnpm lint"
    platform: "node"
  test:
    command: "pnpm test"
    platform: "node"

Then project-level configs can import or extend this. The exact mechanism depends on how you structure your Moonrepo setup, but the goal is the same: shared, version-controlled task conventions.


5. workspace-tool-configuration files (ESLint, Prettier, TS, etc.)

Not Moon-specific, but critical for standardizing conventions when used with Moon tasks.

Files to commit:

  • .eslintrc.cjs / .eslintrc.json
  • .prettierrc / .prettierrc.cjs
  • tsconfig.base.json and project-level tsconfig.json
  • Babel configs, Jest configs, etc.

Why commit them:

  • Moon will often call these tools as part of tasks defined in moon.yml.
  • Having them versioned ensures that moon run :lint behaves the same across machines/teams.

Example integration in Moon:

tasks:
  lint:
    command: "pnpm eslint src --max-warnings=0"
    inputs:
      - "src/**/*"
      - ".eslintrc.cjs"
      - "tsconfig.json"

6. moon.d / config directory (if you use modular config)

If you split configuration into multiple files (e.g., per language or per domain), you should commit the entire configuration directory.

Typical contents:

  • moon.d/node.yml
  • moon.d/rust.yml
  • moon.d/frontend.yml
  • Shared task presets

Why commit them:

  • Encourages reuse of config modules across teams.
  • Keeps configuration structured, maintainable, and consistent.
  • Serves as documentation of how the monorepo is organized.

Moonrepo config files you should NOT commit

To keep your repo lean and avoid environment-specific noise, exclude:

1. Cache directories

  • .moon/cache/
  • .moon/outputs/ (or any configured outputs directory)
  • Tool-specific caches inside .moon

These are generated artifacts and can be safely ignored in your VCS.

Add to .gitignore:

.moon/cache/
.moon/outputs/
.moon/logs/

2. Environment- or machine-specific overrides

If you use any local override mechanisms (e.g., moon.local.yml or similar patterns), treat them like .env.local:

  • Do not commit.
  • Document them in a template file (e.g., moon.local.example.yml) if needed.

3. Editor and IDE-specific files

These are not Moonrepo-specific, but worth emphasizing:

  • .vscode/
  • .idea/
  • .DS_Store

They don’t belong in Moon’s shared task/config story.


Recommended .gitignore entries for a Moonrepo

To standardize behavior across teams, include a shared .gitignore containing at least:

# Moonrepo
.moon/cache/
.moon/outputs/
.moon/logs/

# Node
node_modules/
npm-debug.log*
pnpm-lock.yaml.*
yarn-error.log*

# Builds
dist/
build/
coverage/

# OS / IDE clutter
.DS_Store
.idea/
.vscode/

This ensures every clone behaves the same and doesn’t pollute Git history with ephemeral artifacts.


How these files standardize tasks and conventions across teams

By committing the core Moonrepo configuration files, you gain several concrete benefits:

1. Shared task vocabulary

  • Every project has the same task names (lint, test, build, release).

  • Developers can switch repos or teams and immediately know how to interact with any app/package.

  • CI can use generic commands such as:

    moon run :lint
    moon run :test
    moon run :build
    

    without per-project scripting.

2. Consistent toolchains and environments

  • moon.yml / toolchain.yml ensures all developers use the same Node/Rust/etc. versions.
  • Eliminates “works on my machine” discrepancies.
  • CI environments can be simplified to “install Moon, run Moon tasks.”

3. Enforced conventions at scale

  • Project-level moon.yml files define types (application, library, tool) and tags (frontend, backend, infra), driving:
    • Dependency graphs
    • Affected-task runs
    • Policy enforcement (e.g., frontend apps can’t depend on infra packages)
  • Conventions become configuration, not tribal knowledge.

4. Easier onboarding and documentation

  • New teams can copy an existing project’s moon.yml and adjust only what’s unique.
  • Shared task templates and tool configs serve as living documentation of how the org expects work to be done.

Example layout for a well-structured Moonrepo

Here’s a sample structure that many teams find scalable:

.
├─ moon.yml                 # Root workspace config
├─ toolchain.yml            # Shared toolchain versions (optional but recommended)
├─ .gitignore
├─ config/
│  ├─ tasks/
│  │  ├─ frontend.yml       # Shared tasks for frontend projects
│  │  └─ backend.yml        # Shared tasks for backend projects
│  ├─ lint/
│  │  └─ .eslintrc.cjs
│  └─ tsconfig.base.json
├─ apps/
│  ├─ web/
│  │  ├─ moon.yml           # Project-level config
│  │  ├─ tsconfig.json
│  │  └─ src/
│  └─ api/
│     ├─ moon.yml
│     └─ src/
├─ packages/
│  ├─ ui/
│  │  └─ moon.yml
│  └─ utils/
│     └─ moon.yml
└─ .moon/
   ├─ cache/                # Ignored
   └─ outputs/              # Ignored

Everything that controls behavior is committed; only ephemeral data is ignored.


Quick checklist: which Moonrepo configs to commit

When setting up your repository, use this checklist to verify you’re standardizing tasks and conventions effectively:

Commit these:

  • moon.yml at repo root
  • toolchain.yml or toolchain section in moon.yml
  • Project-level moon.yml (or project.yml) files for every app/package
  • Any moon.d/ or config/ directories that define shared tasks or settings
  • Workspace-wide linting, formatting, and TypeScript configs (.eslintrc, .prettierrc, tsconfig.base.json, etc.)
  • A shared .gitignore that respects Moon’s cache/output directories

Ignore these:

  • .moon/cache/, .moon/outputs/, .moon/logs/
  • Machine-specific or local override configs (e.g., moon.local.yml)
  • Editor/IDE and OS-specific files

By committing these Moonrepo configuration files and ignoring only ephemeral artifacts, your organization can enforce consistent tasks, tooling, and conventions across all teams—making your monorepo predictable, maintainable, and easy to scale.