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

> Discover how Trivy parallel scanning enhances performance using its generic pipeline and worker goroutines for faster vulnerability analysis. Learn more about context cancellation.

- Repository: [Aqua Security/trivy](https://github.com/aquasecurity/trivy)
- Tags: deep-dive
- Published: 2026-03-23

---

**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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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.

```go
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`](https://github.com/aquasecurity/trivy/blob/main/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

```go
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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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:

```bash
trivy image --parallel 8 nginx:latest

```

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

```go
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:

```go
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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/pkg/flag/scan_flags.go) and mirrored in [`pkg/parallel/walk.go`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/pkg/fanal/artifact/image/image.go) and Kubernetes manifest scanning in [`pkg/k8s/scanner/scanner.go`](https://github.com/aquasecurity/trivy/blob/main/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.