DevSecOps · Guide · 2026

Building a Secure CI/CD Pipeline with GitHub Actions and Trivy

A practical, opinionated walkthrough for shifting security left — from source commit to production deploy — using GitHub Actions, Trivy, OIDC, and a handful of small, boring guardrails that actually work.

Why DevSecOps

Traditional pipelines treat security as a gate at the end. DevSecOps flips that: every commit is scanned, every dependency is checked, every image is verified before it reaches production. The goal is not to add friction — it is to catch the boring 90% of issues automatically so humans can focus on the interesting 10%.

Pipeline Stages

  1. Source — signed commits, branch protection, required reviews.
  2. Static analysis — CodeQL / Semgrep for SAST.
  3. Dependencies — Trivy or Dependabot for SCA + license checks.
  4. Build — reproducible container images, pinned base images.
  5. Image scan — Trivy scan on the built image, fail on HIGH/CRITICAL.
  6. SBOM — generate + attach a Software Bill of Materials.
  7. Deploy — OIDC to cloud, no long-lived keys, immutable tags.
  8. Runtime — monitoring, alerting, and periodic re-scans.

A Minimal Secure Workflow

The workflow below runs on every push. It installs dependencies, runs a filesystem scan with Trivy, builds a Docker image, scans the image, and uploads the SARIF report to GitHub's Security tab.

name: ci-secure

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  security-events: write
  id-token: write        # for OIDC to AWS/GCP

jobs:
  build-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Trivy filesystem scan
        uses: aquasecurity/trivy-action@0.24.0
        with:
          scan-type: fs
          scan-ref: .
          severity: HIGH,CRITICAL
          exit-code: '1'
          ignore-unfixed: true
          format: sarif
          output: trivy-fs.sarif

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-fs.sarif

      - name: Build image
        run: docker build -t app:${{ github.sha }} .

      - name: Trivy image scan
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: app:${{ github.sha }}
          severity: HIGH,CRITICAL
          exit-code: '1'
          ignore-unfixed: true

Secrets Management

  • Never commit .env — use GitHub Encrypted Secrets or a cloud secret store.
  • Prefer OIDC over long-lived cloud keys (AWS configure-aws-credentials, GCP Workload Identity).
  • Rotate anything that ever appeared in a log. Assume it is compromised.
  • Run gitleaks in CI to catch accidental secret commits.

Container Hardening

  • Use minimal base images (distroless, alpine, or chainguard).
  • Pin by digest, not by tag: FROM node@sha256:....
  • Run as a non-root USER. Drop Linux capabilities.
  • Set readOnlyRootFilesystem: true in Kubernetes.

SBOM & Supply Chain

Generate an SBOM with trivy sbom or syft, attach it to the release, and sign artifacts with cosign. This gives you a queryable inventory the day a new CVE drops — instead of grepping repositories at 2am.

Failing Loudly

A scan that warns is a scan that gets ignored. Set exit-code: 1 on HIGH/CRITICAL and let the pipeline fail. Then triage — either fix, upgrade, or explicitly waive with a tracked .trivyignore entry and an expiry date.

Beyond the Pipeline

  • Re-scan running images on a schedule — CVEs are published continuously.
  • Ship logs and metrics (Prometheus + Grafana, or a hosted stack).
  • Practice restoring from backups. An untested backup is a hope.

Wrap-up

A secure pipeline is not a single tool — it is a chain of small, enforced defaults. Start with Trivy + branch protection + OIDC, add SBOMs and image signing next, and layer on runtime monitoring once the basics are boring. The best security work is invisible.

Written by Piyush Prasad — Cloud & DevOps Engineer. Back to portfolio.