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

> Discover how Trivy manages caching with Redis, memory, and filesystem backends. Learn to optimize scans by preventing redundant downloads and efficiently storing artifact metadata.

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

---

**Trivy abstracts storage through a unified `Cache` interface in [`pkg/cache/cache.go`](https://github.com/aquasecurity/trivy/blob/main/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)](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)](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)](https://github.com/aquasecurity/trivy/blob/main/pkg/flag/cache_flags.go).

```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)](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)](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)](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

```bash

# 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)

```go
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 types**—`ArtifactInfo` and `BlobInfo`—through a unified interface in [`pkg/cache/cache.go`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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`](https://github.com/aquasecurity/trivy/blob/main/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.