---
title: Daemon
toc_max_heading_level: 4
tags: [daemon, performance, background]
---

import VersionLabel from '@site/src/components/Docs/VersionLabel';

<VersionLabel version="2.2.0" header />

As workspaces grow in size, the time spent building
[project and task graphs](../how-it-works/project-graph) becomes a noticeable bottleneck. Every
`moon` CLI invocation rebuilds these graphs from scratch, even when nothing has changed since the
last run. The daemon solves this by running a background server process that keeps the workspace
graph hot in memory, watches for file changes, and rebuilds only when necessary. This eliminates
redundant work and significantly improves CLI response times, especially in larger workspaces with
hundreds or thousands of projects/tasks.

:::caution

This feature is currently **unstable** and must be explicitly enabled. Its behavior and
configuration may change in future releases.

:::

## Enabling the daemon

To enable the daemon, set the [`unstable_daemon`](../config/workspace#daemon) setting in
[`.moon/workspace.yml`](../config/workspace).

```yaml title=".moon/workspace.yml"
unstable_daemon: true
```

Alternatively, you can enable it via the `MOON_DAEMON` environment variable without modifying your
configuration. This is useful for trying it out before committing to the change.

```shell
MOON_DAEMON=true moon run :build
```

## How it works

### Background server

When the daemon is enabled, the first `moon` CLI command you run automatically spawns a background
server process. This process detaches from the terminal and continues running after the command
completes. Subsequent CLI invocations connect to the running daemon instead of starting fresh.

The daemon communicates with CLI processes over a local IPC channel — Unix domain sockets on
macOS/Linux, and named pipes on Windows. All daemon files (state, socket, and logs) are stored in
`.moon/cache/daemon`. The daemon records its metadata in a `daemon.json` state file, and takes exclusive ownership of its workspace through an
advisory file lock held for its entire lifetime. A crashed daemon releases the lock automatically,
so a stale socket or state file can no longer block or misdirect the next start.

If the daemon fails to start or is unavailable, the CLI falls back to building graphs in-process as
it normally would without the daemon. You won't see an error — the CLI simply proceeds without the
performance benefit.

Connecting to the daemon and starting it are a single operation — a command that needs
the daemon will start one itself if a background pre-warm hasn't already, with concurrent starts
coordinating so only one daemon is ever spawned. The daemon also retires itself after a long idle
period with no requests, and exits immediately if its workspace is deleted, so an abandoned
workspace no longer leaves a daemon running forever.

### File watching

To keep the in-memory graph up to date, the daemon watches the workspace root recursively for file
system changes. Events are debounced (coalesced within a short window) to avoid redundant rebuilds
during rapid edits.

The following directories are always ignored by the watcher:

- `.git`, `.svn`, `node_modules`
- `.moon/cache`, `.moon/docker`

Changes to the following files and directories trigger an asynchronous graph rebuild:

- [`.prototools`](../proto/config) — reloads toolchain configuration
- `.moon/workspace.*` — reloads workspace settings
- `.moon/toolchains.*` — reloads toolchain plugins
- `.moon/extensions.*` — reloads extension plugins
- `.moon/tasks/**/*` — reloads task inheritance
- `moon.*` config files in project directories — reloads project configuration
- Creation or removal of project directories matching configured
  [project sources](../config/workspace#projects)

Because rebuilds happen asynchronously in the background, the daemon remains responsive to new CLI
connections while a rebuild is in progress.

## Managing the daemon

The [`moon daemon`](../commands/daemon) command provides subcommands for managing the daemon's
lifecycle. In most cases you won't need these — the daemon starts automatically and stays out of the
way — but they're useful for debugging or manual control.

### Starting

```shell
$ moon daemon start
```

Starts the daemon if it's not already running. If a daemon is already running for this workspace,
the existing process is reused. You typically don't need to run this manually, as the daemon
auto-starts with any `moon` command when enabled. See
[`moon daemon start`](../commands/daemon/start).

### Stopping

```shell
$ moon daemon stop
```

Stops the daemon gracefully. If the daemon does not shut down within a few seconds, it will be
forcefully killed. Daemon files (state, socket) are cleaned up automatically. See
[`moon daemon stop`](../commands/daemon/stop).

### Restarting

```shell
$ moon daemon restart
```

Stops the running daemon and starts a new one. This is useful after manual configuration changes, or
if the daemon's cached state seems stale. See [`moon daemon restart`](../commands/daemon/restart).

### Checking status

```shell
$ moon daemon status
```

Displays information about the running daemon, including its process ID, uptime, socket/pipe
endpoint, and file paths for the state and log files. Whether the daemon is running is determined by
connecting to it, rather than probing a process ID. See
[`moon daemon status`](../commands/daemon/status).

### Viewing logs

```shell
$ moon daemon logs
```

Tails the daemon's log file in real time. The daemon must be running for this command to work. The
log file is located at `.moon/cache/daemon/server.log` and contains detailed trace-level output. As
of v2.4, log files are rotated up to 7 times, with older files automatically deleted. See
[`moon daemon logs`](../commands/daemon/logs).

## Troubleshooting

### The daemon won't start

- Verify the daemon is enabled: check that `unstable_daemon: true` is set in
  [`.moon/workspace.yml`](../config/workspace), or that the `MOON_DAEMON` environment variable is
  set.
- Check if a daemon is already running with [`moon daemon status`](../commands/daemon/status).
- As of v2.4, a crashed daemon releases its workspace lock automatically, so a stale state file no
  longer blocks startup. If startup still fails, delete `.moon/cache/daemon` and try again.

### The graph seems stale

If tasks or projects appear to be missing or outdated despite config changes, the file watcher may
have missed an event. Run [`moon daemon restart`](../commands/daemon/restart) to force a fresh graph
build.

### Where are the logs?

The daemon writes detailed logs to `.moon/cache/daemon/server.log`. Use
[`moon daemon logs`](../commands/daemon/logs) to tail this file, or open it directly in an editor.

### After upgrading moon

As of v2.4, this is handled automatically — when connecting, the CLI checks the running daemon's
moon and protocol version against its own, and restarts it on a mismatch, so a daemon left over from
before a `moon upgrade` is replaced instead of serving the old binary indefinitely. If you ever need
to force it, restart the daemon manually with
[`moon daemon restart`](../commands/daemon/restart).

### CI environments

The daemon is designed for local development where a persistent background process can be reused
across multiple CLI invocations. In CI environments, where each run starts fresh, the daemon
provides no benefit since there's no long-lived process to connect to. Enabling it in CI won't cause
harm, but it won't improve performance either.

## Limitations

- This feature is **unstable** and may change in future releases. Behavior, configuration, and CLI
  commands are subject to change.
- Each workspace runs its own daemon process. If you work across multiple workspaces, each will have
  a separate daemon.
- On Windows, the daemon uses named pipes instead of Unix domain sockets. The behavior is
  equivalent, but the underlying IPC mechanism differs.
