
moonrepo: what config files should we commit to standardize tasks and conventions across teams?
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,buildwith the same semantics. - Teams can rely on
moon run web:buildormoon run :testacross 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.cjstsconfig.base.jsonand project-leveltsconfig.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 :lintbehaves 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.ymlmoon.d/rust.ymlmoon.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 :buildwithout per-project scripting.
2. Consistent toolchains and environments
moon.yml/toolchain.ymlensures 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.ymlfiles 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.ymland 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.ymlat repo root -
toolchain.ymlor toolchain section inmoon.yml - Project-level
moon.yml(orproject.yml) files for every app/package - Any
moon.d/orconfig/directories that define shared tasks or settings - Workspace-wide linting, formatting, and TypeScript configs (
.eslintrc,.prettierrc,tsconfig.base.json, etc.) - A shared
.gitignorethat 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.