Blog · 2026-10-30 · Gardience Engineering

Gardience in your pipeline: Jenkins, GitHub Actions, and GitLab CI

Check fleet health before a deploy, run a playbook in check mode on every merge request, and run approved changes from Jenkins, GitHub Actions, or GitLab CI with the gdn command line and the REST API.

Most teams already have a pipeline. Gardience fits into it with two pieces you already have on your server:

Both use an API key, so the pipeline needs no SSH keys or WinRM access to your machines. Every change still goes through Gardience's permission checks, approvals, change windows, and audit log. If the key requires approval, the job waits while a second person approves the run.

Set up once

  1. In the console, open Settings > API keys and create a key. A read key is enough for checks. An operate key can run things. Give each pipeline its own key, so the audit log shows which pipeline did what.
  2. Store the key in your CI system's secret store as GARDIENCE_KEY, and the console address as GARDIENCE_URL.
  3. If the console uses a self-signed certificate, store config/tls/gardience.crt from the server as well, and point GARDIENCE_CA at it.
  4. Run the job on an agent or runner that can reach the console. Gardience runs inside your network, so this is usually a self-hosted runner.

gdn needs only Python 3. It reads GARDIENCE_URL, GARDIENCE_KEY, and GARDIENCE_CA from the environment, so no login step is needed.

Exit codes are what a pipeline needs: 0 success, 1 the request could not be made, 2 the run finished but did not succeed.

GitHub Actions

Use the gdn action:

name: deploy
on:
  pull_request:
  push:
    branches: [main]

jobs:
  playbook:
    runs-on: [self-hosted, linux]
    steps:
      - uses: actions/checkout@v4
      - uses: gardience/gdn-action@v1
        with:
          server: ${{ vars.GARDIENCE_URL }}
          api-key: ${{ secrets.GARDIENCE_KEY }}
          ca-cert: ${{ vars.GARDIENCE_CA }}
          args: playbook site.yml -l web ${{ github.event_name == 'pull_request' && '--check' || '' }} --reason "${{ github.repository }}@${{ github.sha }}"

Pull requests get a dry run. A merge to main runs it for real.

Jenkins

pipeline {
  agent { label 'linux' }
  environment {
    GARDIENCE_URL = 'https://gardience.example.com'
    GARDIENCE_KEY = credentials('gardience-key')
    GARDIENCE_CA  = credentials('gardience-ca')   // a "secret file" credential
  }
  stages {
    stage('Get gdn') {
      steps {
        sh 'curl -fsSL --cacert "$GARDIENCE_CA" -o gdn "$GARDIENCE_URL/gardience/cli/gdn" && chmod +x gdn'
      }
    }
    stage('Check') {
      steps { sh './gdn playbook site.yml -l web --check --reason "$JOB_NAME #$BUILD_NUMBER"' }
    }
    stage('Apply') {
      when { branch 'main' }
      steps { sh './gdn playbook site.yml -l web --reason "$JOB_NAME #$BUILD_NUMBER"' }
    }
  }
}

GitLab CI

stages: [check, apply]

.gdn:
  image: python:3.12-slim
  tags: [inside-network]
  before_script:
    - apt-get update -qq && apt-get install -y -qq curl >/dev/null
    - curl -fsSL --cacert "$GARDIENCE_CA" -o gdn "$GARDIENCE_URL/gardience/cli/gdn" && chmod +x gdn

check:
  extends: .gdn
  stage: check
  script:
    - ./gdn playbook site.yml -l web --check --reason "$CI_PROJECT_PATH!$CI_MERGE_REQUEST_IID"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

apply:
  extends: .gdn
  stage: apply
  script:
    - ./gdn playbook site.yml -l web --reason "$CI_PROJECT_PATH@$CI_COMMIT_SHORT_SHA"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Set GARDIENCE_CA as a variable of type File so GitLab writes it to disk and passes the path.

A compliance gate

To stop a deploy when machines are out of compliance or have stopped reporting, read /api/v1/fleet with a read key:

bad=$(curl -fsS --cacert "$GARDIENCE_CA" -H "Authorization: Bearer $GARDIENCE_KEY" \
  "$GARDIENCE_URL/gardience/api/v1/fleet" \
  | jq -r '.nodes[] | select(.node | startswith("web")) | select(.compliant == false or .stale) | .node')
if [ -n "$bad" ]; then echo "Not compliant: $bad"; exit 1; fi

Each node in the answer also carries pending_updates, reboot_pending, baseline_score, and failing_controls, so you can gate on whichever matters to you. See the REST API reference.

Other things a pipeline can do

GoalCommand
Run a command on a groupgdn run web 'systemctl is-active nginx'
Run a runbookgdn runbook restart-service -e nodegroup=web -e service=nginx
Start without waitingadd --no-wait, then gdn status RUN_ID later
Fleet statusgdn nodes web

To manage Gardience's own setup as code (runbooks, node groups, schedules, change windows), use the Terraform provider in the same pipeline.

If you want a Gardience plugin for your CI system instead of these few lines, tell us which one.

Try Gardience on your own machines

One supported console for Windows and Linux on open-source Salt: patching, a scored STIG/CIS-aligned baseline, drift repair, event-driven automation, two-person approval and a tamper-evident audit log. Flat yearly pricing, not per node.

Free 90-day trial (25 nodes)See it work Compare

Salt is a trademark of its owner, Ansible is a trademark of Red Hat, Inc., and other names are trademarks of their owners. Gardience is independent and not affiliated with any of them.