> For the complete documentation index, see [llms.txt](https://docs-lunar.earthly.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-lunar.earthly.dev/configuration/lunar-config/validation.md).

# Validating your config

`lunar hub pull` validates configuration on the Hub, but by then the change has already merged. To catch mistakes **before** they merge, validate the config in CI on the pull request.

There are two complementary tools:

1. **`lunar hub pull --dry-run`** — the authoritative check, meant to run in CI on your pull requests. It runs the Hub's own load-and-validate steps against the same code (load the config, resolve every `uses:` plugin, validate the whole manifest, and resolve each component's default branch) and stops before applying anything — so it catches the mistakes a real `hub pull` would reject after merge, without a separate re-implementation that could drift.
2. **A JSON Schema** — generated from the manifest definition, for editor autocomplete and inline structural feedback while you type.

{% hint style="info" %}
The dry run needs **no Hub connection**. It does need **GitHub access** (a `LUNAR_GITHUB_TOKEN`, or a configured Hub used only as an auth source) to resolve `uses:` plugins — the same access the Hub itself uses to fetch them. This is what lets it catch plugin-resolution problems that a purely structural check would miss.
{% endhint %}

## `lunar hub pull --dry-run`

```bash
lunar hub pull --dry-run <repo>
```

`<repo>` is the config repository ref, e.g. `github://acme-corp/lunar@my-branch`. The dry run clones it, loads `lunar-config.yml` (and any [`lunar-config.d/`](/configuration/lunar-config.md) fragments), resolves every `uses:` plugin, and runs the full manifest validation — required fields, enum values, hook/cron shape, [`on:`](/configuration/lunar-config/on.md) expressions, [domain](/configuration/lunar-config/domains.md) references, and whether each referenced plugin actually exists and resolves. It exits non-zero and prints the failure if anything is wrong, and **never** contacts the Hub or applies the manifest.

It runs the Hub's own `PullManifest` load-and-validate code — the `Fetch` pipeline plus the same component default-branch resolution the Hub performs before persisting — so a component pointing at a typo'd or inaccessible repo fails the dry run too, not just a real pull.

{% hint style="warning" %}
**One known gap:** the dry run does not install per-snippet dependencies (the `pip`/`npm` install the Hub runs for inline, non-image snippets), because that needs the runtime images and is too heavy for a CI gate. A dependency-install failure can therefore still surface only on a real `hub pull`. Everything else — structure, plugin resolution, and component branch resolution — is validated.
{% endhint %}

## Pre-merge validation in CI

Run the dry run on every pull request against your config repo. The example below is for **GitHub Actions** — it's provided as a starting point; adapt the CLI install and the invocation to whatever CI system you use. It needs a token that can read the config repo and the plugin repos it references.

It intentionally runs on **all** pull requests rather than only when `lunar-config.yml` changes: the dry run resolves and validates every referenced plugin, including local `./`-path plugins in `collectors/`, `policies/`, or `catalogers/`, so a PR that edits one of those without touching `lunar-config.yml` is still checked. (A `paths:` filter would skip those.)

```yaml
name: Validate Lunar config

on:
  pull_request:

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: Install Lunar CLI
        run: |
          curl -fsSL -o lunar "https://github.com/earthly/lunar-dist/releases/latest/download/lunar-linux-amd64"
          chmod +x lunar
          sudo mv lunar /usr/local/bin/lunar
      - name: Dry-run the config
        env:
          LUNAR_GITHUB_TOKEN: ${{ secrets.LUNAR_GITHUB_TOKEN }}
        run: |
          lunar hub pull --dry-run "github://${{ github.repository }}@${{ github.event.pull_request.head.sha }}"
```

{% hint style="info" %}
Pin the CLI to a specific release (e.g. `.../releases/download/v1.2.3/lunar-linux-amd64`) instead of `latest` for reproducible CI. The token needs read access to the config repo and any private plugin repos it `uses:`.
{% endhint %}

## JSON Schema (editor autocomplete)

For editor autocomplete and inline structural validation as you edit, the CLI emits a [JSON Schema](https://json-schema.org/) (draft-07 format) for `lunar-config.yml`, generated from the same manifest definition the Hub uses:

```bash
lunar config schema > lunar-config.schema.json
```

Commit that file and point your editor at it. In VS Code, for example:

```jsonc
// .vscode/settings.json
{
  "yaml.schemas": {
    "./lunar-config.schema.json": ["lunar-config.yml", "lunar-config.d/*.yml"]
  }
}
```

{% hint style="warning" %}
The JSON Schema is a **structural aid only** — it checks unknown/misspelled keys, types, and enum values. It does **not** resolve `uses:` plugins or component branches (the dry run does that), so it can't tell you a plugin or repo doesn't exist. (Neither the schema nor the dry run installs per-snippet dependencies — see the dry-run note above.) Use `lunar hub pull --dry-run` as the authoritative check; treat the schema as editor feedback.

If you also validate against the schema in CI, use a **YAML 1.2** validator such as [`check-jsonschema`](https://check-jsonschema.readthedocs.io/). YAML 1.1 parsers (e.g. Python's `yaml.safe_load`) coerce bare `on:`, `yes:`, `no:`, and `off:` mapping keys into booleans, which spuriously fails schema validation on the `on:` field.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs-lunar.earthly.dev/configuration/lunar-config/validation.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
