← Back to DevBytes

Grype Vulnerability Scanner: Complete Implementation Guide

What is Grype?

Grype is an open-source vulnerability scanner developed by Anchore that specializes in identifying known security vulnerabilities in container images, filesystems, and software packages. It works by analyzing the software components present in your artifacts and cross-referencing them against multiple curated vulnerability databases to surface actionable findings.

Unlike traditional scanners that rely solely on Common Vulnerabilities and Exposures (CVE) databases, Grype integrates data from numerous sources including NIST's National Vulnerability Database (NVD), GitHub Security Advisories, Red Hat's security data, Alpine SecDB, and more. This multi-source approach significantly improves coverage and reduces false negatives.

Grype is designed to be fast, easy to integrate, and works seamlessly with Syft — Anchore's Software Bill of Materials (SBOM) generator. Together, they form a powerful toolkit for generating and analyzing SBOMs in modern software supply chain security workflows.

Key Features at a Glance

Why Grype Matters for Modern Development

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Software supply chain attacks have increased dramatically. In recent years, high-profile incidents involving compromised dependencies, malicious container images, and zero-day exploits have underscored the critical need for continuous vulnerability scanning throughout the development lifecycle. Grype addresses this need by providing:

Shift-Left Security Validation

By integrating Grype early in development, teams can identify vulnerabilities before code reaches production. Scanning container images during CI builds, for instance, allows developers to catch and remediate issues at the pull request stage rather than discovering them post-deployment when remediation costs are highest.

SBOM-Driven Transparency

Grype works hand-in-hand with SBOM generation tools like Syft. An SBOM is a formal, machine-readable inventory of all components in a software artifact. Grype consumes these SBOMs to produce precise vulnerability reports, enabling organizations to comply with emerging regulatory requirements such as Executive Order 14028 in the United States, which mandates SBOM usage for government software suppliers.

Continuous Compliance

For teams operating in regulated industries, Grype's structured output formats (JSON, SARIF) make it straightforward to integrate findings into compliance dashboards, ticketing systems, and automated enforcement pipelines. You can fail builds based on vulnerability severity thresholds, ensuring only compliant artifacts reach production.

Installation Guide

Grype is distributed as a single static binary with no runtime dependencies, making installation trivial across platforms. Here are the recommended installation methods:

macOS / Linux via curl

# Download and install the latest version
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

# Verify the installation
grype version

Using Homebrew (macOS / Linux)

brew tap anchore/grype
brew install grype

# Verify
grype version

Docker Image (No Local Installation Required)

# Run Grype directly via Docker
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  anchore/grype:latest docker-image alpine:latest

Windows via Chocolatey

choco install grype

# Verify
grype version

Manual Download

Pre-built binaries for all platforms are available on the GitHub releases page. Download the appropriate archive, extract it, and place the binary in your PATH.

Core Usage Patterns

Scanning a Container Image

The most common use case is scanning a container image directly from a registry or your local Docker daemon:

# Scan a remote image from Docker Hub
grype docker-image alpine:latest

# Scan an image from a private registry
grype docker-image myregistry.example.com/app:v1.2.3

# Scan a locally tagged image
grype docker-image my-local-image:dev

Grype will pull the image, catalog its software packages, and match them against vulnerability databases. Results are displayed as a formatted table by default, showing the vulnerability ID, severity, affected package, installed version, and fix version where available.

Scanning a Local Directory or Filesystem

You can scan any directory containing software artifacts — this is particularly useful for scanning application dependencies, virtual machine images, or source repositories with vendored libraries:

# Scan the current directory
grype dir .

# Scan a specific directory
grype dir /path/to/your/project

# Scan an unpacked container image filesystem
grype dir ./extracted-rootfs

Scanning from an SBOM File

If you have already generated an SBOM with Syft or another tool, you can feed it directly to Grype for vulnerability analysis:

# Generate an SBOM with Syft and pipe to Grype
syft alpine:latest -o json | grype

# Or scan a saved SBOM file
grype sbom:./my-sbom.spdx.json

# CycloneDX format is also supported
grype sbom:./my-sbom.cyclonedx.xml

This decoupled workflow is powerful: you can generate SBOMs once and scan them repeatedly as vulnerability databases are updated, without re-accessing the original artifact.

Output Formats and Reporting

Grype supports several output formats to suit different integration needs. The default is a human-readable table, but structured formats are essential for automation.

JSON Output

# Produce detailed JSON output
grype docker-image alpine:latest -o json

# Save to a file
grype docker-image alpine:latest -o json > grype-results.json

The JSON output includes the full vulnerability data including descriptions, URLs, affected package details, and CVSS scores. This is the preferred format for programmatic processing and integration with dashboards.

SARIF Output

SARIF (Static Analysis Results Interchange Format) is a standardized format supported by GitHub, GitLab, Azure DevOps, and many other platforms:

grype docker-image alpine:latest -o sarif > results.sarif

# Upload to GitHub Code Scanning
# results.sarif can be uploaded via the GitHub API or actions

CycloneDX Output

Grype can output vulnerability-enriched CycloneDX documents, embedding vulnerability findings directly into the SBOM format:

grype docker-image alpine:latest -o cyclonedx-json > vuln-sbom.json

Customizing Table Output

# Add CVE descriptions to the table view
grype docker-image alpine:latest -o table --add-cpes

# Show only critical and high vulnerabilities
grype docker-image alpine:latest --severity critical,high

# Exclude negligible findings
grype docker-image alpine:latest --exclude-severity negligible

Filtering and Suppressing Results

Severity-Based Filtering

Use the --severity flag to limit results to specific severity levels. This is particularly useful in CI pipelines where you may want to fail only on critical findings:

# Only show critical and high vulnerabilities
grype docker-image app:latest --severity critical,high

# Exclude negligible and unknown severity
grype docker-image app:latest --exclude-severity negligible,unknown

# Fail build only on critical vulnerabilities
grype docker-image app:latest --fail-on critical

Vulnerability Allowlists (Suppression)

Not all vulnerabilities are immediately exploitable in your specific context. Grype supports allowlist configurations to suppress findings that have been reviewed and accepted:

# Using a YAML configuration file
grype docker-image app:latest -c grype-allowlist.yaml

Example grype-allowlist.yaml:

# grype-allowlist.yaml
ignore:
  # Suppress a specific CVE across all packages
  - vulnerability: CVE-2023-12345
    type: cve
    reason: "Reviewed: This CVE requires local access which is not available"
    
  # Suppress based on package name and version
  - package:
      name: libssl
      version: 1.1.1k
    type: package
    reason: "Vendor-confirmed patch backported, version string not updated"
    
  # Suppress based on path within the image
  - path: /usr/local/lib/debug/*
    type: path
    reason: "Debug symbols only, not deployed in production"
    
  # Suppress an entire namespace of vulnerabilities
  - namespace: debian:unstable
    type: namespace
    reason: "Testing-only packages, not used at runtime"

The allowlist mechanism is essential for keeping scan results actionable — without it, teams often suffer from alert fatigue as the same accepted risks appear repeatedly.

Database Management

Grype maintains a local vulnerability database that is automatically downloaded on first use and periodically updated. Understanding database management helps ensure accurate results.

Checking Database Status

# View current database information
grype db status

# Sample output:
# Location: /home/user/.cache/grype/db/5
# Built: 2024-01-15 08:30:00 UTC
# Schema version: 5
# Update available: No

Updating the Database

# Force update the vulnerability database
grype db update

# Check for updates without downloading
grype db check

Database Location

The database cache is stored at ~/.cache/grype/db/ on Linux/macOS. In CI environments, you may want to cache this directory between runs to avoid repeated downloads:

# Example GitHub Actions cache configuration
# Cache key: grype-db-{runner.os}
# Path: ~/.cache/grype/db/

CI/CD Pipeline Integration

GitHub Actions Integration

Here's a complete GitHub Actions workflow that scans container images during CI:

name: Container Vulnerability Scan
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  vulnerability-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        
      - name: Install Grype
        run: |
          curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | \
            sh -s -- -b ${{ github.workspace }}/bin
          echo "${{ github.workspace }}/bin" >> $GITHUB_PATH
          
      - name: Build container image
        run: |
          docker build -t my-app:${{ github.sha }} .
          
      - name: Scan image with Grype
        run: |
          grype docker-image my-app:${{ github.sha }} \
            --severity critical,high \
            --fail-on critical \
            -o sarif > grype-results.sarif
        continue-on-error: true
        
      - name: Upload SARIF results to GitHub
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: grype-results.sarif
          
      - name: Check for critical vulnerabilities
        run: |
          grype docker-image my-app:${{ github.sha }} \
            --severity critical \
            --fail-on critical \
            -o json > critical-check.json
          echo "If we reach here, no critical vulnerabilities found"

GitLab CI Integration

# .gitlab-ci.yml
vulnerability-scan:
  stage: security
  image: docker:24
  services:
    - docker:24-dind
  variables:
    DOCKER_HOST: tcp://docker:2376
    DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - apk add curl
    - curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - grype docker-image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --severity critical,high --fail-on critical -o json > scan-report.json
  artifacts:
    paths:
      - scan-report.json
    expire_in: 30 days
  allow_failure: false

Jenkins Pipeline (Declarative)

// Jenkinsfile
pipeline {
    agent any
    environment {
        GRYPE_VERSION = 'latest'
    }
    stages {
        stage('Install Grype') {
            steps {
                sh '''
                    curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | \
                    sh -s -- -b /usr/local/bin
                '''
            }
        }
        stage('Build Image') {
            steps {
                sh 'docker build -t my-app:${BUILD_TAG} .'
            }
        }
        stage('Vulnerability Scan') {
            steps {
                sh '''#!/bin/bash
                    grype docker-image my-app:${BUILD_TAG} \
                        --severity critical,high \
                        --fail-on critical \
                        -o json > grype-scan-${BUILD_TAG}.json
                '''
                archiveArtifacts artifacts: "grype-scan-${BUILD_TAG}.json"
            }
        }
        stage('Evaluate Results') {
            steps {
                sh '''#!/bin/bash
                    CRITICAL_COUNT=$(grype docker-image my-app:${BUILD_TAG} \
                        --severity critical -o json | jq '.matches | length')
                    if [ "$CRITICAL_COUNT" -gt 0 ]; then
                        echo "Found $CRITICAL_COUNT critical vulnerabilities - failing build"
                        exit 1
                    fi
                    echo "No critical vulnerabilities found"
                '''
            }
        }
    }
}

Advanced Configuration

Configuration File Reference

Grype supports a YAML configuration file that can be placed at standard locations or specified explicitly:

# Default locations Grype checks (in order):
# 1. .grype.yaml (current directory)
# 2. .grype/config.yaml (current directory)
# 3. ~/.grype/config.yaml (user home)
# 4. XDG config path

# Or specify explicitly
grype docker-image app:latest -c /path/to/custom-config.yaml

Example comprehensive configuration:

# .grype.yaml
# Vulnerability database options
db:
  # Auto-update check interval (default: true)
  auto-update: true
  
  # Maximum number of days between database updates
  max-update-age-days: 5
  
  # Custom database cache location
  cache-dir: /shared/cache/grype-db

# Check for available updates on startup
check-for-app-update: false

# Default output settings
output: json

# Severity filter applied globally
severity:
  - critical
  - high
  - medium

# Allowlist configuration
ignore:
  - vulnerability: CVE-2022-0001
    type: cve
    reason: "Accepted risk - requires privileged access"
    
  - package:
      name: curl
      version: 7.79.1
    type: package
    reason: "Patched internally, version not bumped"

# Registry authentication
registry:
  auth:
    - url: https://my-registry.example.com
      username: ${REGISTRY_USER}
      password: ${REGISTRY_PASSWORD}
      # Or use token-based auth
      # token: ${REGISTRY_TOKEN}
      
# Mirror configuration for air-gapped environments
mirror: https://internal-mirror.example.com/grype-db

Air-Gapped and Offline Environments

For environments without internet access, Grype supports offline database updates:

# Step 1: On an internet-connected machine, export the database
grype db export /tmp/grype-db-export.tar.gz

# Step 2: Transfer the archive to your air-gapped environment

# Step 3: Import the database on the offline machine
grype db import /path/to/grype-db-export.tar.gz

# Step 4: Verify import
grype db status

# Step 5: Scan with --offline flag to prevent any network calls
grype docker-image my-internal-image:latest --offline

Working with Syft for SBOM-Based Workflows

Grype and Syft are designed as complementary tools. Here's how to build a complete SBOM-driven vulnerability management pipeline:

Generate SBOM and Scan in One Step

# Pipe Syft output directly to Grype
syft packages docker-image my-app:latest -o json | grype

# Generate SBOM, save it, then scan it later
syft packages docker-image my-app:latest -o spdx-json > sbom.spdx.json
grype sbom:sbom.spdx.json -o json > vulnerability-report.json

Continuous SBOM Scanning Script

#!/bin/bash
# scan-artifact.sh - Generate SBOM and scan for vulnerabilities
set -e

ARTIFACT_IMAGE="${1:-alpine:latest}"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
SBOM_DIR="./sboms"
REPORT_DIR="./reports"

mkdir -p "$SBOM_DIR" "$REPORT_DIR"

echo "=== Generating SBOM for $ARTIFACT_IMAGE ==="
SBOM_FILE="$SBOM_DIR/sbom-${TIMESTAMP}.spdx.json"
syft packages "docker-image:$ARTIFACT_IMAGE" -o spdx-json > "$SBOM_FILE"
echo "SBOM saved to $SBOM_FILE"

echo "=== Scanning SBOM for vulnerabilities ==="
REPORT_FILE="$REPORT_DIR/vuln-report-${TIMESTAMP}.json"
grype "sbom:$SBOM_FILE" -o json > "$REPORT_FILE"
echo "Vulnerability report saved to $REPORT_FILE"

echo "=== Summary ==="
CRITICAL=$(jq '[.matches[] | select(.vulnerability.severity == "Critical")] | length' "$REPORT_FILE")
HIGH=$(jq '[.matches[] | select(.vulnerability.severity == "High")] | length' "$REPORT_FILE")
TOTAL=$(jq '.matches | length' "$REPORT_FILE")
echo "Total vulnerabilities: $TOTAL"
echo "Critical: $CRITICAL | High: $HIGH"

Programmatic Usage via the Go Library

For teams building custom security tooling in Go, Grype can be used as an embedded library. This allows you to integrate vulnerability scanning directly into your applications:

package main

import (
    "context"
    "fmt"
    "os"
    
    "github.com/anchore/grype/grype"
    "github.com/anchore/grype/grype/db"
    "github.com/anchore/grype/grype/match"
    "github.com/anchore/grype/grype/vulnerability"
    "github.com/anchore/syft/syft"
    "github.com/anchore/syft/syft/source"
)

func main() {
    ctx := context.Background()
    
    // Step 1: Generate a software catalog using Syft's library
    src, err := source.NewFromDockerImage("alpine:latest", "")
    if err != nil {
        fmt.Fprintf(os.Stderr, "failed to create source: %v\n", err)
        os.Exit(1)
    }
    
    catalog, _, _, err := syft.CatalogPackages(src, syft.DefaultConfig())
    if err != nil {
        fmt.Fprintf(os.Stderr, "failed to catalog: %v\n", err)
        os.Exit(1)
    }
    
    // Step 2: Load vulnerability database
    dbConfig := db.Config{
        DBPath: "~/.cache/grype/db/5",
    }
    vulnStore, err := db.NewVulnerabilityProvider(dbConfig)
    if err != nil {
        fmt.Fprintf(os.Stderr, "failed to load db: %v\n", err)
        os.Exit(1)
    }
    
    // Step 3: Run vulnerability matching
    matches, err := grype.FindVulnerabilities(
        ctx,
        vulnStore,
        catalog,
        grype.VulnerabilityMatcherConfig{
            MatchByPackage: true,
            MatchByCPE:     true,
        },
    )
    if err != nil {
        fmt.Fprintf(os.Stderr, "failed to find vulnerabilities: %v\n", err)
        os.Exit(1)
    }
    
    // Step 4: Process results
    for _, m := range matches {
        fmt.Printf("Vulnerability: %s | Severity: %s | Package: %s\n",
            m.Vulnerability.ID,
            m.Vulnerability.Severity,
            m.Package.Name,
        )
    }
    fmt.Printf("Total matches found: %d\n", len(matches))
}

Best Practices for Production Deployments

1. Establish Severity Thresholds and SLAs

Define clear policies for vulnerability remediation based on severity. A common approach:

2. Implement Layered Scanning

Don't rely on a single scan point. Implement multiple scanning gates:

# Pre-commit: Scan dependencies in the source tree
grype dir .

# CI build: Scan the built container image
grype docker-image built-image:${CI_COMMIT_SHA}

# Registry admission: Scan before promoting to production registry
grype docker-image registry.example.com/app:staging

# Continuous production monitoring: Periodic re-scanning
# Schedule a cron job to re-scan deployed images weekly

3. Maintain an Active Allowlist

Keep your allowlist configuration in version control alongside your application code. Require peer review for allowlist additions. Each entry should include a mandatory reason and an expiration date:

# .grype.yaml committed to repository
ignore:
  - vulnerability: CVE-2023-XXXXX
    type: cve
    reason: "Reviewed by security team on 2024-01-15. Internal network isolation mitigates exposure."
    expires: 2024-07-15  # Require re-review after 6 months

4. Cache the Vulnerability Database

In CI environments, cache the Grype database to reduce scan time and avoid rate limiting from upstream sources:

# GitHub Actions cache example
- name: Cache Grype DB
  uses: actions/cache@v4
  with:
    path: ~/.cache/grype/db
    key: grype-db-${{ runner.os }}-${{ hashFiles('**/go.sum') }}
    restore-keys: |
      grype-db-${{ runner.os }}-

# Then run Grype - it will only update if cache is stale
- run: grype docker-image my-app:latest --fail-on critical

5. Combine Grype with Additional Scanners

Grype excels at vulnerability detection, but no single tool catches everything. Combine it with complementary tools:

6. Automate SBOM Generation and Archiving

Generate and store SBOMs for every build. These serve as an audit trail and enable rapid impact analysis when new vulnerabilities are disclosed:

#!/bin/bash
# Generate and archive SBOM for every release
RELEASE_TAG="${1}"
syft packages docker-image:my-app:${RELEASE_TAG} \
  -o cyclonedx-json > sbom-${RELEASE_TAG}.cdx.json

# Store in artifact repository with metadata
# Consider tools like OCI registries for SBOM storage
oras push my-registry.example.com/sboms/my-app:${RELEASE_TAG} \
  sbom-${RELEASE_TAG}.cdx.json:application/vnd.cyclonedx+json

7. Integrate Findings into Developer Workflow

Don't let vulnerability reports sit in a silo. Surface findings where developers work:

Troubleshooting Common Issues

Database Update Failures

# If database update fails, check connectivity
grype db check --verbose

# Force a fresh database download
rm -rf ~/.cache/grype/db/
grype db update

# If behind a proxy, set environment variables
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
grype db update

Image Pull Failures

# For private registries, configure authentication
grype docker-image private.registry.example.com/app:latest \
  -c registry-config.yaml

# registry-config.yaml
registry:
  auth:
    - url: https://private.registry.example.com
      username: ${REGISTRY_USER}
      password: ${REGISTRY_PASSWORD}

# Or use Docker credential helpers
# Grype respects ~/.docker/config.json by default

False Positives and Missing Matches

# Enable verbose output to see matching decisions
grype docker-image app:latest -vv

# Check which vulnerability sources are active
grype db status --verbose

# If a known vulnerability is not detected, verify:
# 1. The package is actually present (use syft to list packages)
# 2. The vulnerability exists in Grype's data sources
# 3. The version comparison is working correctly

Conclusion

Grype has established itself as a foundational tool in modern software supply chain security. Its combination of speed, multi-source vulnerability data, flexible output formats, and tight integration with SBOM workflows makes it an excellent choice for teams looking to implement continuous vulnerability scanning without adding significant friction to their development process.

The key to success with Grype is not just running scans, but embedding them thoughtfully into your development lifecycle. Start by establishing severity-based policies, implementing scanning at multiple stages (source, build, registry, and production), and maintaining a disciplined allowlist practice. Combine Grype with complementary tools like Syft for SBOM generation and Cosign for artifact signing to build a comprehensive supply chain security posture.

As regulatory requirements around SBOMs and vulnerability disclosure continue to evolve, tools like Grype will become increasingly essential. Investing in these practices today positions your team to meet tomorrow's compliance demands while meaningfully reducing your attack surface. The complete implementation patterns covered in this guide — from basic scanning through CI/CD integration, air-gapped deployment, and programmatic embedding — provide a solid foundation for building security into every stage of your software delivery pipeline.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles