How Trivy Parallel Scanning Boosts Performance: A Deep Dive into the Generic Pipeline

Trivy accelerates vulnerability scans by orchestrating a configurable pool of worker goroutines through a generic pipeline implementation that processes independent tasks concurrently while respecting context.Context cancellation.

Trivy parallel scanning is the core mechanism that allows Aqua Security's open-source scanner to analyze container images, Kubernetes manifests, and filesystem artifacts at scale. According to the aquasecurity/trivy source code, the tool achieves this through a reusable pipeline architecture defined in pkg/parallel/pipeline.go, which coordinates work distribution across multiple CPU cores while preventing resource exhaustion through configurable limits and semaphore-based throttling.

Configuring Parallelism with the --parallel Flag

Trivy exposes scan concurrency via the --parallel CLI flag, defined in pkg/flag/scan_flags.go. The flag accepts an integer specifying the number of goroutines (default: 5) and supports auto-detection when set to 0.

ParallelFlag = Flag[int]{
    Name:          "parallel",
    ConfigName:    "scan.parallel",
    Default:       5,
    Usage:         "number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism",
    TelemetrySafe: true,
}

When parsed, the value populates flag.Options.Parallel and propagates downstream to individual scanners. Setting --parallel 0 triggers automatic configuration based on runtime.GOMAXPROCS, allowing Trivy to match available CPU capacity without manual tuning.

The Generic Pipeline Architecture

All major Trivy scanners rely on a type-safe Pipeline[T, U] struct in pkg/parallel/pipeline.go. This generic implementation creates:

  • An input channel (itemCh) feeding work items of type T
  • A configurable number of workers (numWorkers) consuming from itemCh
  • An output channel (results) collecting processed values of type U
  • A final onResult consumer running in the main goroutine for lock-free aggregation
type Pipeline[T, U any] struct {
    numWorkers int
    items      []T
    onItem     func(context.Context, T) (U, error)
    onResult   func(U) error
    progress   bool
}

The pipeline uses errgroup.WithContext for automatic error propagation and cancellation. If any worker returns an error, the shared context cancels immediately, aborting remaining goroutines while preserving the first encountered error for deterministic failure handling.

Where Parallel Scanning Is Applied

Trivy applies parallel scanning across multiple subsystems, each adapting the pipeline to specific resource constraints:

Kubernetes Scanner The scanner in pkg/k8s/scanner/scanner.go creates a pipeline where each worker executes s.scanVulns on individual container images referenced in cluster manifests. Notably, when using the filesystem cache backend (cache.TypeFS), Trivy forces workers = 1 to avoid bbolt lock contention.

Image Artifact Analysis In pkg/fanal/artifact/image/image.go, parallel processing handles:

  • Uncompressed size calculation across layers
  • OS detection spanning multiple layer keys
  • Per-layer content analysis

Filesystem and VM Artifacts For I/O-heavy operations, Trivy uses semaphore.New(a.artifactOption.Parallel) to cap concurrent filesystem operations, preventing resource overload during layer extraction or directory walks.

Default Walk Utility The pkg/parallel/walk.go file defines defaultParallel = 5 as a fallback for filesystem traversal when no explicit value is provided.

Cancellation and Error Handling

The pipeline's errgroup integration ensures cooperative cancellation. When Pipeline.Do(ctx) executes:

  1. Workers process items from itemCh until exhaustion or context cancellation
  2. Results stream to onResult in the main goroutine, eliminating need for additional synchronization primitives
  3. The first error triggers immediate context cancellation across all workers
  4. Do returns the initial error, maintaining predictable failure semantics

This design prevents partial result corruption while allowing scanners to fail fast when encountering critical errors.

Practical Implementation Examples

Run a scan with 8 concurrent workers from the CLI:

trivy image --parallel 8 nginx:latest

Programmatically construct a parallel pipeline using Trivy's internal utilities:

package main

import (
	"context"
	"fmt"

	"github.com/aquasecurity/trivy/pkg/parallel"
)

type layer string

func main() {
	ctx := context.Background()
	layers := []layer{"layerA", "layerB", "layerC"}

	p := parallel.NewPipeline(
		4,                     // numWorkers
		false,                 // disable progress bar
		layers,                // items to process
		func(_ context.Context, l layer) (string, error) {
			// Analyze container layer
			return fmt.Sprintf("processed %s", l), nil
		},
		func(res string) error {
			fmt.Println(res)    // Safe aggregation in main goroutine
			return nil
		},
	)

	if err := p.Do(ctx); err != nil {
		panic(err)
	}
}

Adjust parallelism programmatically when initializing the scanner:

opts := flag.Options{
    Parallel: 10, // 10 workers for this scan
}
scanner := scanner.NewScanner("my-cluster", runner, opts)
report, err := scanner.Scan(context.Background(), artifacts)

Summary

  • Trivy parallel scanning leverages a generic Pipeline[T, U] type in pkg/parallel/pipeline.go to coordinate worker goroutines across vulnerability scans.
  • The --parallel flag (default: 5) controls concurrency, with 0 enabling auto-detection via runtime.GOMAXPROCS.
  • Scanners automatically reduce workers to 1 when using the filesystem cache to prevent database lock contention.
  • The errgroup-based implementation provides automatic cancellation and deterministic error handling.
  • Semaphores cap I/O concurrency for filesystem and VM artifact processing, preventing resource exhaustion.

Frequently Asked Questions

How does Trivy handle errors during parallel scanning?

Trivy uses errgroup.WithContext inside pkg/parallel/pipeline.go to manage worker lifecycles. When any worker returns an error, the shared context cancels immediately, causing all other goroutines to abort. The Pipeline.Do method returns the first encountered error, ensuring scans fail fast without producing partial results.

Can I disable parallel scanning in Trivy?

Yes, set --parallel 1 to force single-threaded execution. Additionally, Trivy automatically disables parallelism (forcing one worker) when using the filesystem cache backend for Kubernetes scanning, as implemented in pkg/k8s/scanner/scanner.go, to avoid bbolt database lock contention.

What is the default parallelism value in Trivy?

The default value is 5, defined in pkg/flag/scan_flags.go and mirrored in pkg/parallel/walk.go as defaultParallel. You can override this with the --parallel flag or set it to 0 to enable auto-detection based on available CPU cores via runtime.GOMAXPROCS.

Which Trivy operations benefit most from parallel scanning?

Container image layer analysis in pkg/fanal/artifact/image/image.go and Kubernetes manifest scanning in pkg/k8s/scanner/scanner.go see the greatest performance gains, as these involve independent analysis of multiple layers or images. Filesystem scanning uses semaphores to balance parallelism with I/O constraints.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →