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.

VariableDescription
MCD_DEFAULT_API_IDMonte Carlo API key ID
MCD_DEFAULT_API_TOKENMonte Carlo API token

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 variableUsed inDescription
MC_MONITORS_NAMESPACE--namespace flagNamespace to apply monitors to
PROJECT_DIR--project-dir flagPath to the directory containing montecarlo.yml (optional if running from project root)

Core Command

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) 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

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():

      - 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

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

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 (or as project environment variables). To set other values on a step, use CircleCI's native environment: key rather than inline shell assignment:

      - 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:

# 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.

# 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:

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:

      - 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 for details.

Example Repositories