Skip to content

CI/CD Integration

kubectl-audit works well as a pre-deploy gate or post-deploy health check. The audit summary goes to stderr; stdout is clean JSON/YAML for scripting.

Exit codes

Exit code Meaning
0 Command ran successfully (even if issues were found — the plugin reports, not enforces)
non-zero Command failed (API error, permission denied, invalid flag)

Enforcing as a gate

kubectl-audit returns exit code 0 even when issues are found — it is a reporter, not a policy enforcer. To gate on findings, check the count via -o json and jq (see examples below).

Shell scripting

Gate on pod health

# Fail the script if any unhealthy pods exist in a namespace
ISSUES=$(kubectl audit pods -n my-namespace -o json 2>/dev/null | jq '.items | length')
if [ "$ISSUES" -gt 0 ]; then
  echo "❌ $ISSUES unhealthy pod(s) found"
  kubectl audit pods -n my-namespace
  exit 1
fi
echo "✅ All pods healthy"

Post-deploy check

# Wait a few seconds after deploy, then audit
sleep 15
kubectl audit pods -n production -o json 2>/dev/null \
  | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name): \(.status.phase)"'

GitHub Actions

Pre-deploy gate

.github/workflows/deploy.yml
name: deploy
on:
  push:
    branches: [main]

jobs:
  pre-deploy-audit:
    runs-on: ubuntu-latest
    steps:
      - name: Set up kubectl
        uses: azure/setup-kubectl@v4

      - name: Install kubectl-audit
        run: |
          kubectl krew index add codenio https://github.com/codenio/codenio-krew-index.git
          kubectl krew install codenio/audit

      - name: Pre-deploy health check
        env:
          KUBECONFIG: ${{ secrets.KUBECONFIG }}
        run: |
          ISSUES=$(kubectl audit pods -n production -o json 2>/dev/null | jq '.items | length')
          if [ "$ISSUES" -gt 0 ]; then
            echo "::error::$ISSUES unhealthy pod(s) in production — aborting deploy"
            kubectl audit pods -n production
            exit 1
          fi

  deploy:
    needs: pre-deploy-audit
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        run: echo "Deploying..."

Post-deploy audit comment on PR

.github/workflows/audit-comment.yml
name: audit-comment
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - name: Install kubectl-audit
        run: |
          kubectl krew index add codenio https://github.com/codenio/codenio-krew-index.git
          kubectl krew install codenio/audit

      - name: Run audit
        id: audit
        env:
          KUBECONFIG: ${{ secrets.KUBECONFIG }}
        run: |
          OUTPUT=$(kubectl audit pods -A 2>&1)
          echo "result<<EOF" >> $GITHUB_OUTPUT
          echo "$OUTPUT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Cluster Audit\n\`\`\`\n${{ steps.audit.outputs.result }}\n\`\`\``
            })

GitLab CI

.gitlab-ci.yml
audit:
  stage: validate
  image: bitnami/kubectl:latest
  before_script:
    - kubectl krew index add codenio https://github.com/codenio/codenio-krew-index.git
    - kubectl krew install codenio/audit
  script:
    - |
      ISSUES=$(kubectl audit pods -n $CI_ENVIRONMENT_SLUG -o json 2>/dev/null | jq '.items | length')
      if [ "$ISSUES" -gt 0 ]; then
        kubectl audit pods -n $CI_ENVIRONMENT_SLUG
        exit 1
      fi
  environment:
    name: production

Parsing output with jq

# Names of unhealthy pods
kubectl audit pods -A -o json 2>/dev/null | jq -r '.items[].metadata.name'

# Pods grouped by namespace
kubectl audit pods -A -o json 2>/dev/null \
  | jq 'group_by(.metadata.namespace) | map({namespace: .[0].metadata.namespace, count: length})'

# Services with no backing pods
kubectl audit service -A -o json 2>/dev/null \
  | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"'