Hamburger Cross Icon
GitOps Guardrails - Lunar Policy for Deployment And Infrastructure

GitOps Guardrails

Tool-agnostic GitOps best practices over normalized .cd.gitops: automated sync (prune + self-heal), source/destination allow-lists, and adoption coverage to flag components that should be on GitOps but aren't. Works for any GitOps collector (argocd today, flux later).

Add gitops to your lunar-config.yml:
uses: github://earthly/lunar-lib/policies/gitops@v1.0.5

Included Guardrails

This policy includes 4 guardrails that enforce standards for your deployment and infrastructure.

Guardrail

sync-policy

Requires Applications to use an automated sync policy with prune and self-heal enabled. Drift between Git and the cluster is corrected automatically instead of accumulating silently.

argocd sync policy automated sync prune self-heal drift
View Guardrail
Guardrail

source-repo-allowlist

Requires each Application's manifest source repository to be within a configured allow-list of trusted repositories. Prevents deployments from unreviewed or untrusted Git sources.

argocd source repo allowlist supply chain trusted source
View Guardrail
Guardrail

destination-allowlist

Requires each Application's destination namespace and cluster to be within a configured allow-list. Stops Applications from targeting clusters or namespaces they should not deploy to.

argocd destination namespace cluster allowlist
View Guardrail
Guardrail

gitops-managed

Coverage check: requires every component this policy targets (optionally narrowed to a tag) to actually be deployed by an ArgoCD Application — i.e. to have resolved .cd.gitops data, whether parsed locally or pushed by the argocd-deployment-tracking collector from a separate GitOps repo. Surfaces components that should be on GitOps but haven't been migrated. Unlike the other checks, absence of GitOps data is a failure here, not a skip — that's the whole point.

argocd gitops adoption coverage migration deployment visibility
View Guardrail

How Guardrails Fit into Lunar

Lunar guardrails define your engineering standards as code. They evaluate data collected by integrations and produce pass/fail checks with actionable feedback.

Policies support gradual enforcement—from silent scoring to blocking PRs or deployments—letting you roll out standards at your own pace without disrupting existing workflows.

Learn How Lunar Works
1
Integrations Gather Data
Collectors extract metadata from code, CI pipelines, tool outputs, and scans
2
{ } Centralized as JSON
All data merged into each component's unified metadata document
3
Guardrails Enforce Standards This Policy
Real-time feedback in PRs and AI workflows

Required Integrations

This policy evaluates data gathered by one or more of the following integration(s). Make sure to enable them in your lunar-config.yml.

Configuration

Configure this policy in your lunar-config.yml.

Inputs

Input Required Default Description
allowed_source_repos Required Comma-separated allow-list of permitted Application source repoURLs for source-repo-allowlist. Required when that check is enabled (errors if empty).
allowed_destinations Required Comma-separated allow-list of permitted destinations for destination-allowlist. Each entry is `namespace` (allowed on any cluster) or `cluster/namespace` (pinned to one cluster — the cluster segment matches the Application destination's registered name or its server URL). Required when that check is enabled (errors if empty).
expected_tag Required For gitops-managed — only enforce coverage on components whose catalog tags (.catalog.entity.tags, e.g. from a Backstage catalog-info.yaml) include this value. Empty enforces on every component the policy targets (via on:).

Documentation

View on GitHub

GitOps Guardrails

Tool-agnostic GitOps continuous-delivery best practices, for any GitOps tool.

Overview

This policy holds the GitOps checks that aren't tied to a specific tool — they read the normalized .cd.gitops paths, so they work for any GitOps collector (ArgoCD today, Flux later). It enforces automated sync with prune and self-heal, source-repo and destination allow-lists, and GitOps adoption coverage. Because the argocd-deployment-tracking collector pushes .cd.gitops onto a source component from a separate GitOps repo, these guardrails apply on the service repo, not just the GitOps repo. The config checks skip cleanly when a component has no GitOps data; the gitops-managed coverage check inverts that, failing components that should be on GitOps but aren't. Pair it with the argocd policy for ArgoCD-specific checks (CRD validation, AppProject hygiene).

Policies

This plugin provides the following guardrails (use include to select a subset):

Policy Description Failure Meaning
sync-policy Requires automated sync with prune and self-heal Application sync policy is manual or missing prune/self-heal
source-repo-allowlist Requires the manifest source repo to be allow-listed Application source repoURL is not in the allow-list
destination-allowlist Requires the destination namespace/cluster to be allow-listed Application targets a namespace or cluster outside the allow-list
gitops-managed Requires targeted components to actually be GitOps-managed (coverage) A component that should be on GitOps has no resolved .cd.gitops data

Required Data

This policy reads from the following Component JSON paths:

Path Type Provided By
.cd.gitops object argocd collector (presence = GitOps-managed; read by gitops-managed)
.cd.gitops.applications[] array argocd collector
.cd.gitops.applications[].sync_policy object argocd collector
.cd.gitops.applications[].destination object argocd collector
.cd.gitops.applications[].source_ref object argocd collector
.catalog.entity.tags[] array catalog collector (only when gitops-managed uses expected_tag)

Note: Ensure the argocd collector is configured before enabling this policy. The gitops-managed check decides which components are "expected on GitOps" two ways: deploy it on: the tag/domain that must be GitOps-managed (e.g. on: [production]) and leave expected_tag empty, or set expected_tag to a catalog tag and the check only enforces on components whose .catalog.entity.tags carry it (populated by a catalog cataloger, e.g. from a Backstage catalog-info.yaml).

Installation

Add to your lunar-config.yml:

collectors:
  - uses: github://earthly/lunar-lib/collectors/argocd@v1.0.0
    on: [gitops]

policies:
  # Tool-agnostic GitOps checks
  - uses: github://earthly/lunar-lib/policies/gitops@v1.0.0
    on: [gitops]
    enforcement: report-pr
    # include: [sync-policy, gitops-managed]  # Only run specific checks
    # with:
    #   allowed_source_repos: "https://github.com/org/gitops.git"
    #   allowed_destinations: "payments,checkout"
    #   expected_tag: "should-be-gitops"

  # ArgoCD-specific checks (recommended alongside)
  - uses: github://earthly/lunar-lib/policies/argocd@v1.0.0
    on: [gitops]
    enforcement: report-pr

Examples

Passing Example

An Application with automated sync, an allow-listed source, and an allowed destination:

{
  "cd": {
    "gitops": {
      "applications": [
        {
          "name": "payment-api",
          "path": "apps/payment-api.yaml",
          "sync_policy": {"automated": true, "prune": true, "self_heal": true},
          "destination": {"server": "https://kubernetes.default.svc", "namespace": "payments"},
          "source_ref": {"repoURL": "https://github.com/org/gitops.git"}
        }
      ]
    }
  }
}

Failing Example

An Application with manual sync and no prune/self-heal:

{
  "cd": {
    "gitops": {
      "applications": [
        {
          "name": "my-app",
          "path": "apps/my-app.yaml",
          "sync_policy": {"automated": false, "prune": false, "self_heal": false},
          "destination": {"server": "https://kubernetes.default.svc", "namespace": "payments"},
          "source_ref": {"repoURL": "https://github.com/org/gitops.git"}
        }
      ]
    }
  }
}

Failure message: apps/my-app.yaml: Application 'my-app' should use an automated sync policy with prune and self-heal

Coverage Failure (gitops-managed)

A component the org expects on GitOps, with no resolved .cd.gitops — nothing parsed locally and nothing pushed by argocd-deployment-tracking from the GitOps repo:

{
  "vcs": { "provider": "github" }
}

Failure message: Component is expected to be GitOps-managed but no GitOps Application deploys it (.cd.gitops not found). Migrate it to GitOps or exclude it from this policy's scope.

Remediation

When this policy fails, resolve it by:

  1. For sync-policy failures: Enable automated sync with prune: true and selfHeal: true (spec.syncPolicy.automated in ArgoCD).
  2. For source-repo-allowlist failures: Point the Application's source repoURL at an allow-listed repository, or add the repository to allowed_source_repos.
  3. For destination-allowlist failures: Set the destination to an allow-listed namespace/cluster, or extend allowed_destinations.
  4. For gitops-managed failures: Either migrate the component to GitOps (create an Application that deploys it, so argocd-deployment-tracking resolves and stamps .cd.gitops), or narrow the policy's scope (on: targeting, or expected_tag) so it isn't expected on GitOps. This check converges once argocd-deployment-tracking has processed the GitOps repo and pushed the link.

Consumers who want any of these surfaced without blocking can pin enforcement: report-pr at config time.

Open Source

This policy is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.

View Repository

Ready to Automate Your Standards?

See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 200+ built-in guardrails.

Works with any process
check AI agent rules & prompt files
check Post-mortem action items
check Security & compliance policies
check Testing & quality requirements
Automate Now
Paste your AGENTS.md or manual process doc and get guardrails in minutes
Book a Demo