How Trivy Handles Caching: Redis vs. Memory vs. Filesystem Backends Explained

Trivy abstracts storage through a unified Cache interface in pkg/cache/cache.go, offering three interchangeable backends—in-memory (MemoryCache), filesystem BoltDB (FSCache), and Redis (RedisCache)—that persist artifact metadata and layer blobs to eliminate redundant downloads across scans.

Scanning container images repeatedly wastes bandwidth and time when the same layers must be re-fetched from registries. According to the aquasecurity/trivy source code, the scanner mitigates this by caching two distinct data types—ArtifactInfo (image metadata and scan results) and BlobInfo (per-layer package details)—through a pluggable backend selected via the --cache-backend flag.

Cache Architecture and Data Model

Trivy’s cache system is built around a Go interface that decouples storage implementation from scanning logic. This allows operators to trade off speed, persistence, and sharing capabilities depending on the runtime environment.

The Cache Interface

The contract is defined in [pkg/cache/cache.go](https://github.com/aquasecurity/trivy/blob/main/pkg/cache/cache.go) and specifies six core operations:

  • PutArtifact / GetArtifact – Store and retrieve image metadata.
  • PutBlob / GetBlob – Store and retrieve layer-level package information.
  • MissingBlobs – Identify which artifact or blob IDs are absent from the cache.
  • Clear – Purge all cached data.

Data Types Stored

Type Content Purpose
ArtifactInfo OS family, layer digests, configuration history Avoids re-pulling image manifests.
BlobInfo Installed packages, OS libraries, application dependencies Skips re-analysis of unchanged layers.

Selecting a Cache Backend

Backend selection follows a factory pattern implemented in [pkg/cache/client.go](https://github.com/aquasecurity/trivy/blob/main/pkg/cache/client.go), which reads CLI flags defined in [pkg/flag/cache_flags.go](https://github.com/aquasecurity/trivy/blob/main/pkg/flag/cache_flags.go).

switch opts.CacheBackend {
case cache.TypeMemory:
    return cache.NewMemoryCache()
case cache.TypeFS:
    return cache.NewFSCache(opts.CacheDir)
case cache.TypeRedis:
    return cache.NewRedisCache(opts.RedisBackend,
                               opts.RedisCACert,
                               opts.RedisCert,
                               opts.RedisKey,
                               opts.RedisTLS,
                               opts.TTL)
}

Available flags include:

  • --cache-backend – Choose memory, fs, or redis.
  • --cache-dir – Filesystem path (BoltDB database location).
  • --redis-backend – Redis URL (redis://host:port/db).
  • --redis-tls, --redis-ca-cert, --redis-cert, --redis-key – TLS configuration.
  • --cache-ttl – Expiration duration for Redis entries (default 0 for no expiry).

In-Memory Cache (Transient)

The MemoryCache implementation in [pkg/cache/memory.go](https://github.com/aquasecurity/trivy/blob/main/pkg/cache/memory.go) provides zero-latency storage for ephemeral scanning workflows.

  • Storage: Two sync.Map instances (artifactBucket, blobBucket) keyed by ID.
  • Operations: Store for writes, Load for reads with type assertion; missing entries return wrapped xerrors.Errorf instances.
  • Lifecycle: Data exists only for the process duration. Calling Clear re-initializes the maps.

This backend is ideal for one-off CI jobs or local development where persistence is unnecessary and maximum speed is prioritized.

Filesystem Cache (Persistent)

The FSCache implementation in [pkg/cache/fs.go](https://github.com/aquasecurity/trivy/blob/main/pkg/cache/fs.go) uses BoltDB to provide durable, local storage.

  • Database: A single fanal.db file created under the directory specified by --cache-dir (default ~/.cache/trivy).
  • Schema: Two buckets—artifactBucket and blobBucket—store JSON-serialized structs.
  • Validation: On retrieval, Trivy checks types.BlobJSONSchemaVersion and ArtifactJSONSchemaVersion to ensure compatibility with the current binary.
  • Expiration: Not automatic; users purge stale data with trivy cache clear, which closes the DB handle and removes the directory.

MissingBlobs performs a sequential scan of the database to filter requested IDs against stored keys, returning a slice of missing digests for the scanner to fetch.

Redis Cache (Distributed)

The RedisCache implementation in [pkg/cache/redis.go](https://github.com/aquasecurity/trivy/blob/main/pkg/cache/redis.go) enables shared caching across multiple Trivy instances using github.com/go-redis/redis/v8.

  • Key Naming: Entries use the prefix fanal:: followed by type and ID:

    
    fanal::artifact::<artifactID>
    fanal::blob::<blobID>
    
  • TTL: The --cache-ttl flag maps to Redis EX flags on SET operations; a zero value creates permanent keys.

  • TLS: When --redis-tls is enabled, the client loads CA, certificate, and key files via GetTLSConfig. If certificates are omitted, a minimal TLS config (server verification only) is used.

  • Clear Operation: Uses the SCAN command with pattern fanal::*, then pipelined UNLINK (non-blocking delete) in batches of 100 keys to avoid blocking the Redis server.

Network round-trips introduce latency, making this backend best suited for distributed CI pipelines where the cost of redundant layer downloads exceeds Redis overhead.

When to Use Each Backend

Scenario Recommended Backend Rationale
Local ad-hoc scans memory No disk I/O or setup; data discarded automatically.
Repeated workstation scans fs Survives reboots; eliminates re-downloads for frequently analyzed images.
Distributed CI/CD pipelines redis Shared state across parallel workers; TTL prevents unbounded growth.
High-security environments redis with TLS Encrypted transport and optional mutual TLS authentication protect data in transit.

Practical Configuration Examples

CLI Usage


# Ephemeral memory cache (fastest, no persistence)

trivy image --cache-backend memory alpine:3.18

# Persistent filesystem cache (default behavior)

trivy image --cache-backend fs --cache-dir /var/cache/trivy alpine:3.18

# Shared Redis cache with 1-hour TTL and mutual TLS

trivy image \
  --cache-backend redis \
  --redis-backend redis://cache.internal:6379/0 \
  --cache-ttl 1h \
  --redis-tls \
  --redis-ca-cert /etc/ssl/ca.pem \
  --redis-cert /etc/ssl/client.crt \
  --redis-key /etc/ssl/client.key \
  alpine:3.18

Programmatic Usage (Go Library)

import "github.com/aquasecurity/trivy/pkg/cache"

// In-memory: transient, zero overhead
memCache := cache.NewMemoryCache()

// Filesystem: persistent across process restarts
fsCache, err := cache.NewFSCache("/opt/trivy-cache")
if err != nil {
    log.Fatal(err)
}

// Redis: remote, TTL-enabled, TLS-secured
redisCache, err := cache.NewRedisCache(
    "redis://redis.example.com:6379/0",
    "/etc/ssl/ca.pem",      // CA certificate
    "/etc/ssl/client.crt",  // Client certificate
    "/etc/ssl/client.key",  // Client key
    true,                   // Enable TLS
    time.Hour,              // TTL
)
if err != nil {
    log.Fatal(err)
}

Summary

  • Trivy caches two data typesArtifactInfo and BlobInfo—through a unified interface in pkg/cache/cache.go.
  • Three backends implement this interface: MemoryCache (ephemeral, sync.Map), FSCache (BoltDB on disk), and RedisCache (remote with TTL).
  • Selection occurs in pkg/cache/client.go based on the --cache-backend flag, with Redis offering additional TLS and TTL controls.
  • Filesystem is the default for standalone CLI usage, while Redis optimizes distributed CI environments through shared state and configurable expiration.

Frequently Asked Questions

How do I switch between cache backends in Trivy?

Use the --cache-backend flag with values memory, fs, or redis. For example, trivy image --cache-backend redis --redis-backend redis://localhost:6379/0 image:tag. The factory logic in pkg/cache/client.go instantiates the corresponding implementation based on this flag.

Does Trivy’s Redis cache support TLS encryption?

Yes. Set --redis-tls to enable TLS, and optionally provide --redis-ca-cert, --redis-cert, and --redis-key for mutual TLS authentication. If only --redis-tls is specified, the client uses a minimal TLS configuration that verifies the server certificate without presenting a client certificate.

What happens when a Redis cache entry reaches its TTL?

When --cache-ttl is specified (e.g., 30m), Trivy passes this duration to Redis during each SET operation. Once the TTL expires, Redis automatically deletes the key. Subsequent scans treat the missing entry as a cache miss and re-download the corresponding layer or artifact data.

Can I use Trivy as a Go library with a custom cache implementation?

Yes. The Cache interface in pkg/cache/cache.go is public, allowing you to implement custom storage backends (e.g., S3, PostgreSQL) by satisfying the PutArtifact, GetArtifact, PutBlob, GetBlob, MissingBlobs, and Clear method signatures. Pass your implementation to the scanner initializer to replace the built-in backends.

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 →