---
updatedAt: 2026-06-03T12:16:42.000Z
agentTools:
  projectIndex: https://docs.getmontecarlo.com/llms.txt
---

# CI/CD Integration

Deploy monitors as code through your CI/CD pipeline.

## Overview

Automate monitor deployments by adding MaC to your CI/CD pipeline. When monitor definitions merge to your main branch, the pipeline installs the CLI and runs `apply` to sync monitors with Monte Carlo. Credentials are passed as environment variables.

## Credentials

The CLI reads API credentials from environment variables. Generate API keys in **Settings > API Keys** in the Monte Carlo UI.

| Variable                | Description            |
| ----------------------- | ---------------------- |
| `MCD_DEFAULT_API_ID`    | Monte Carlo API key ID |
| `MCD_DEFAULT_API_TOKEN` | Monte Carlo API token  |

<br />

Store these as secrets in your CI platform (e.g., GitHub Actions secrets, GitLab CI/CD variables, CircleCI contexts).

The examples below also use shell variables for configuration — these are **not** read by the CLI, they are interpolated into CLI flags by your shell:

| Shell variable          | Used in              | Description                                                                               |
| ----------------------- | -------------------- | ----------------------------------------------------------------------------------------- |
| `MC_MONITORS_NAMESPACE` | `--namespace` flag   | Namespace to apply monitors to                                                            |
| `PROJECT_DIR`           | `--project-dir` flag | Path to the directory containing `montecarlo.yml` (optional if running from project root) |

<br />

## Core Command

```bash
pip install 'montecarlodata>=0.167.0'

montecarlo monitors apply \
  --namespace ${MC_MONITORS_NAMESPACE} \
  --project-dir ${PROJECT_DIR} \
  --auto-yes
```

The CLI reads `MCD_DEFAULT_API_ID` and `MCD_DEFAULT_API_TOKEN` from the environment — set them as secrets in your CI platform (see [Credentials](#credentials)) rather than assigning them on the command line.

`apply` prompts for confirmation by default. In CI, it hangs waiting for stdin — always pass `--auto-yes` to skip the prompt. Never use `--auto-yes` locally — review planned changes before applying.

If your monitors use dbt ref resolution, add `--dbt-manifest <path-to-manifest.json>`.

## GitHub Actions

```yaml
name: Deploy Monitors
on:
  push:
    branches: [main]
    paths:
      - 'monitors/**'
      - 'montecarlo.yml'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install Monte Carlo CLI
        run: pip install 'montecarlodata>=0.167.0'

      - name: Apply monitors
        env:
          MCD_DEFAULT_API_ID: ${{ secrets.MCD_DEFAULT_API_ID }}
          MCD_DEFAULT_API_TOKEN: ${{ secrets.MCD_DEFAULT_API_TOKEN }}
        run: |
          montecarlo monitors apply \
            --namespace production \
            --auto-yes
```

### With dbt

If your monitors are embedded in dbt schema files and use `ref()`:

```yaml
      - name: Compile dbt
        run: dbt compile --project-dir dbt/

      - name: Apply monitors
        env:
          MCD_DEFAULT_API_ID: ${{ secrets.MCD_DEFAULT_API_ID }}
          MCD_DEFAULT_API_TOKEN: ${{ secrets.MCD_DEFAULT_API_TOKEN }}
        run: |
          montecarlo monitors apply \
            --namespace production \
            --dbt-manifest dbt/target/manifest.json \
            --auto-yes
```

## GitLab CI

```yaml
deploy_monitors:
  stage: deploy
  image: python:3.11
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      changes:
        - monitors/**/*
        - montecarlo.yml
  script:
    - pip install 'montecarlodata>=0.167.0'
    - >
      montecarlo monitors apply
      --namespace production
      --auto-yes
  variables:
    MCD_DEFAULT_API_ID: $MCD_DEFAULT_API_ID
    MCD_DEFAULT_API_TOKEN: $MCD_DEFAULT_API_TOKEN
```

## CircleCI

```yaml
version: 2.1

jobs:
  deploy-monitors:
    docker:
      - image: cimg/python:3.11
    steps:
      - checkout
      - run:
          name: Install Monte Carlo CLI
          command: pip install 'montecarlodata>=0.167.0'
      - run:
          name: Apply monitors
          command: |
            montecarlo monitors apply \
              --namespace production \
              --auto-yes

workflows:
  deploy:
    jobs:
      - deploy-monitors:
          filters:
            branches:
              only: main
          context:
            - monte-carlo-prod
```

The `monte-carlo-prod` context injects `MCD_DEFAULT_API_ID` and `MCD_DEFAULT_API_TOKEN` into the job's environment, so the CLI reads them directly — no need to assign them on the command line. Store the credentials in a [CircleCI context](https://circleci.com/docs/contexts/) (or as project environment variables). To set other values on a step, use CircleCI's native `environment:` key rather than inline shell assignment:

```yaml
      - run:
          name: Apply monitors
          environment:
            MC_MONITORS_NAMESPACE: production
          command: |
            montecarlo monitors apply \
              --namespace "${MC_MONITORS_NAMESPACE}" \
              --auto-yes
```

## Validation in PRs

Use `compile` to check YAML syntax, or `apply --dry-run` to run full validation against the Monte Carlo API:

```bash
# Syntax check only — catches malformed YAML and missing monitor blocks (no API credentials needed)
montecarlo monitors compile --namespace production

# Full validation — validates field names, values, table existence, and computes the change diff (requires API credentials)
montecarlo monitors apply --namespace production --dry-run
```

`compile` is faster and does not require API credentials, but it only checks YAML syntax — it does not validate field names, enum values, required fields, or cross-field constraints. Use `apply --dry-run` when you want Monte Carlo to validate the full monitor configuration and compute the actual diff.

```yaml
# GitHub Actions example
- name: Syntax check monitors
  if: github.event_name == 'pull_request'
  # compile checks YAML syntax only — not field names, values, or constraints
  run: |
    montecarlo monitors compile \
      --namespace production

- name: Dry run monitors
  if: github.event_name == 'pull_request'
  env:
    MCD_DEFAULT_API_ID: ${{ secrets.MCD_DEFAULT_API_ID }}
    MCD_DEFAULT_API_TOKEN: ${{ secrets.MCD_DEFAULT_API_TOKEN }}
  run: |
    montecarlo monitors apply \
      --namespace production \
      --dry-run
```

### Complete Workflow: Deploy + PR Validation

A single GitHub Actions file that deploys on push to main and validates on pull requests:

```yaml
name: Monitor Management
on:
  push:
    branches: [main]
    paths:
      - 'monitors/**'
      - 'montecarlo.yml'
  pull_request:
    paths:
      - 'monitors/**'
      - 'montecarlo.yml'

jobs:
  validate:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install 'montecarlodata>=0.167.0'
      - name: Dry run
        env:
          MCD_DEFAULT_API_ID: ${{ secrets.MCD_DEFAULT_API_ID }}
          MCD_DEFAULT_API_TOKEN: ${{ secrets.MCD_DEFAULT_API_TOKEN }}
        run: montecarlo monitors apply --namespace production --dry-run

  deploy:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install 'montecarlodata>=0.167.0'
      - name: Apply monitors
        env:
          MCD_DEFAULT_API_ID: ${{ secrets.MCD_DEFAULT_API_ID }}
          MCD_DEFAULT_API_TOKEN: ${{ secrets.MCD_DEFAULT_API_TOKEN }}
        run: montecarlo monitors apply --namespace production --auto-yes
```

### Monorepo: Using --project-dir

When `montecarlo.yml` is not at the repository root, pass `--project-dir`:

```yaml
      - name: Apply monitors
        run: |
          montecarlo monitors apply \
            --namespace production \
            --project-dir data-team/monitors \
            --auto-yes
```

### Namespace Precedence

> 🚧 If `namespace` is set in `montecarlo.yml`, the `--namespace` CLI flag is silently ignored. Remove the field from `montecarlo.yml` if your CI pipeline needs to control namespace selection via the flag. See [Namespaces](https://docs.getmontecarlo.com/docs/monitors-as-code#namespaces) for details.

## Example Repositories

* [Standalone Monte Carlo project](https://gitlab.com/mkh516/mac)
* [dbt project with monitors (GitLab)](https://gitlab.com/mkh516/dbtSandbox)
* [dbt project with monitors (GitHub)](https://github.com/melissa-hale/dbtSandbox)