Skip to content

6. CI/CD Integration and SLOs-as-Code

Managing SLOs as code is the cornerstone of a scalable SLO practice. It brings the same rigor of version control, code review, and automated deployment that engineering teams apply to application code.

6.1 sloctl: The Nobl9 CLI

sloctl is the Nobl9 command-line tool for managing SLO definitions as YAML files. It supports creating, updating, and deleting all Nobl9 resources, dry-run validation before applying changes, filtering and querying resources by labels, and integration with CI/CD pipelines.

Docs: SLOs as Code

Docs: sloctl on GitHub

Command Description
sloctl apply -f slo.yaml Creates or updates resources defined in the YAML file.
sloctl apply -f slo.yaml --dry-run Validates the YAML without making changes. Essential for CI pipeline validation.
sloctl get slo -p my-project Lists all SLOs in a project.
sloctl get slo -A -l team=checkout Lists all SLOs across all projects matching the specified label.
sloctl delete slo my-slo -p my-project Deletes an SLO. Use with caution in automated pipelines.

6.2 Terraform Provider

For organizations using Terraform for infrastructure management, the Nobl9 Terraform provider provides a declarative way to manage Nobl9 resources alongside infrastructure. It supports projects, services, SLOs, alert policies, alert methods, and data sources using HCL or JSON configuration.

Docs: Nobl9 Terraform Provider

Docs: Terraform Registry: nobl9/nobl9

Docs: Terraform SLO Resource Docs

6.2.1 Terraform Provider Configuration

# providers.tf
terraform {
  required_providers {
    nobl9 = {
      source  = "nobl9/nobl9"
      version = "~> 0.32"
    }
  }
}

provider "nobl9" {
  client_id     = var.nobl9_client_id
  client_secret = var.nobl9_client_secret
  organization  = var.nobl9_org_id
}

6.2.2 Terraform SLO Resource Example

# slo.tf - Payments API Availability SLO
resource "nobl9_project" "checkout_team" {
  display_name = "Checkout Team"
  name         = "checkout-team"
  description  = "SLOs for the checkout engineering team"
  label {
    key    = "team"
    values = ["checkout"]
  }
  label {
    key    = "domain"
    values = ["payments"]
  }
}

resource "nobl9_service" "payments_api" {
  name         = "payments-api"
  display_name = "Payments API"
  project      = nobl9_project.checkout_team.name
  label {
    key    = "tier"
    values = ["critical"]
  }
  label {
    key    = "layer"
    values = ["application"]
  }
}

resource "nobl9_slo" "payments_api_availability" {
  name         = "payments-api-availability"
  display_name = "Payments API Availability"
  project      = nobl9_project.checkout_team.name
  service      = nobl9_service.payments_api.name
  budgeting_method = "Occurrences"

  label {
    key    = "tier"
    values = ["critical"]
  }
  label {
    key    = "layer"
    values = ["application"]
  }

  time_window {
    unit  = "Day"
    count = 30
    is_rolling = true
  }

  objective {
    display_name = "Availability"
    target       = 0.999
    value        = 1
    name         = "availability"
    raw_metric {
      query {
        datadog {
          query = "sum:http.requests.success{service:payments-api}.as_count()"
        }
      }
    }
    count_metrics {
      total {
        datadog {
          query = "sum:http.requests.total{service:payments-api}.as_count()"
        }
      }
      good {
        datadog {
          query = "sum:http.requests.success{service:payments-api}.as_count()"
        }
      }
    }
  }

  alert_policies = [
    nobl9_alert_policy.payments_fast_burn.name,
    nobl9_alert_policy.payments_slow_burn.name,
  ]
}

6.2.3 Terraform CI/CD Pipeline with GitHub Actions

The following GitHub Actions workflow automates Terraform-based SLO management with plan-on-PR and apply-on-merge:

Example: .github/workflows/terraform-slos.yml

name: Terraform SLO Management
on:
  pull_request:
    paths: ['terraform/nobl9/**']
  push:
    branches: [main]
    paths: ['terraform/nobl9/**']

env:
  TF_VAR_nobl9_client_id: ${{ secrets.NOBL9_CLIENT_ID }}
  TF_VAR_nobl9_client_secret: ${{ secrets.NOBL9_CLIENT_SECRET }}
  TF_VAR_nobl9_org_id: ${{ secrets.NOBL9_ORG_ID }}

jobs:
  terraform-plan:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - name: Terraform Init
        run: terraform init
        working-directory: terraform/nobl9
      - name: Terraform Format Check
        run: terraform fmt -check -recursive
        working-directory: terraform/nobl9
      - name: Terraform Plan
        run: terraform plan -no-color
        working-directory: terraform/nobl9

  terraform-apply:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - name: Terraform Init
        run: terraform init
        working-directory: terraform/nobl9
      - name: Terraform Apply
        run: terraform apply -auto-approve
        working-directory: terraform/nobl9

6.3 sloctl CI/CD Pipeline with GitHub Actions

For organizations preferring YAML over HCL, the Nobl9 GitHub Action (nobl9/nobl9-action) provides a pre-built action that uses sloctl:

Docs: Nobl9 GitHub Actions

Docs: Nobl9 GitHub Action on Marketplace

Example: .github/workflows/sloctl-deploy.yml

name: Deploy SLO Definitions
on:
  pull_request:
    paths: ['slo-definitions/**']
  push:
    branches: [main]
    paths: ['slo-definitions/**']

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install yq
        run: |
          sudo wget -qO /usr/local/bin/yq \\
            https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
          sudo chmod +x /usr/local/bin/yq
      - name: Lint Labels
        run: |
          bash scripts/lint-slo-labels.sh \\
            $(find slo-definitions -name '*.yaml' -type f)
      - name: Dry Run Validation
        uses: nobl9/nobl9-action@v1
        with:
          client_id: ${{ secrets.NOBL9_CLIENT_ID }}
          client_secret: ${{ secrets.NOBL9_CLIENT_SECRET }}
          sloctl_yml: slo-definitions/
          dry_run: true

  deploy:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - name: Get Changed Files
        id: changed
        run: |
          FILES=$(git diff --name-only HEAD~1 HEAD -- slo-definitions/)
          echo "files=$FILES" >> $GITHUB_OUTPUT
      - name: Apply Changed Definitions
        uses: nobl9/nobl9-action@v1
        with:
          client_id: ${{ secrets.NOBL9_CLIENT_ID }}
          client_secret: ${{ secrets.NOBL9_CLIENT_SECRET }}
          sloctl_yml: ${{ steps.changed.outputs.files }}

6.4 Repository Structure

slo-definitions/
  projects/
    checkout-team/
      project.yaml
      services/
        payments-api.yaml
      slos/
        payments-api-availability.yaml
        payments-api-latency.yaml
      alert-policies/
        payments-api-fast-burn.yaml
        payments-api-slow-burn.yaml
  shared/
    alert-methods/
      slack-sre.yaml
      pagerduty-critical.yaml
      servicenow-itsm.yaml
  scripts/
    lint-slo-labels.sh
  policy/
    labels.rego              # Conftest policy

6.5 GitOps Best Practices

  • Store all SLO definitions in a dedicated repository or a clearly defined directory within your monorepo.
  • Use branch protection rules to require code reviews on SLO changes.
  • Run sloctl apply --dry-run and label linting on every pull request as mandatory CI checks.
  • Apply only changed files on merge to main (the Procore pattern). This future-proofs the pipeline as you scale.
  • Automate SLO annotation creation in your deployment pipeline to mark each deployment on the SLO timeline.
  • Tag SLO definition changes with version numbers to track changes over time.