Container Scanning in CI/CD Pipelines
February 13, 2026
|
Docker
Security
CI/CD
Trivy in GitHub Actions and Jenkins.
Container Scanning in CI/CD Pipelines
Integrating container image scanning into your CI/CD pipeline is the most effective way to prevent vulnerable images from reaching production. This guide shows how to implement scanning with Trivy in GitHub Actions and Jenkins, with policies that balance security with developer productivity.
Why CI/CD Scanning?
- Shift left — Catch vulnerabilities during build, not in production
- Automated enforcement — No manual review needed for known CVEs
- Developer feedback — Developers see vulnerabilities immediately in PR checks
- Compliance — Automated evidence of vulnerability management for auditors
GitHub Actions with Trivy
name: Build and Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: table
exit-code: 1
severity: CRITICAL,HIGH
ignore-unfixed: true
- name: Run Trivy (SARIF for GitHub Security)
uses: aquasecurity/trivy-action@master
if: always()
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-results.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
- name: Push to ECR (if scan passes)
if: success()
run: |
aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_REGISTRY
docker tag myapp:${{ github.sha }} $ECR_REGISTRY/myapp:${{ github.sha }}
docker push $ECR_REGISTRY/myapp:${{ github.sha }}Jenkins Pipeline
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'docker build -t myapp:${BUILD_NUMBER} .'
}
}
stage('Security Scan') {
steps {
sh '''
trivy image \
--exit-code 1 \
--severity CRITICAL,HIGH \
--ignore-unfixed \
--format json \
--output trivy-report.json \
myapp:${BUILD_NUMBER}
'''
}
post {
always {
archiveArtifacts artifacts: 'trivy-report.json'
publishHTML(target: [
reportName: 'Trivy Report',
reportDir: '.',
reportFiles: 'trivy-report.json'
])
}
}
}
stage('Push') {
when { expression { currentBuild.result == null } }
steps {
sh 'docker push $ECR_REGISTRY/myapp:${BUILD_NUMBER}'
}
}
}
}Scanning Policy
| Severity | Action | Timeline |
|---|---|---|
| CRITICAL | Block deployment | Must fix before merge |
| HIGH | Block deployment | Must fix before merge |
| MEDIUM | Warn (don't block) | Fix within 30 days |
| LOW | Inform only | Fix in next quarter |
.trivyignore for False Positives
Suppress known false positives or accepted risks:
# .trivyignore
# Accepted risk: low-severity glibc issue, no exploit available
CVE-2023-12345
# False positive: not applicable to our runtime
CVE-2023-67890ECR Native Scanning
Enable automatic scanning in ECR as a second layer:
aws ecr put-image-scanning-configuration \
--repository-name myapp \
--image-scanning-configuration scanOnPush=trueBest Practices
- Scan on every build — Not just on main branch, also on PRs
- Use
--ignore-unfixed— Don't block on CVEs with no available fix - Pin base images — Use digest references for reproducible builds
- Rebuild weekly — Pick up security patches in base images
- Track findings over time — Use SARIF integration for trend analysis
Eazy SaaS Tip: We implement a three-layer scanning approach: Trivy in CI/CD (pre-push), ECR scan-on-push (registry), and weekly re-scans of all production images (runtime). This catches vulnerabilities at every stage — from development to production.