# How Deno's Caching Mechanism Works: A Deep Dive into Module and Bytecode Storage

> Explore Deno's caching mechanism. Learn how it stores remote modules, compiled TypeScript, and V8 bytecode using versioned directories and content-addressable storage.

- Repository: [Deno/deno](https://github.com/denoland/deno)
- Tags: deep-dive
- Published: 2026-02-26

---

**Deno's caching mechanism stores remote modules, compiled TypeScript output, and V8 bytecode in a versioned directory structure under `$DENO_DIR`, using deterministic filename hashing for content-addressable storage and SQLite-backed databases for bytecode persistence.**

Deno's performance and reproducibility depend heavily on its sophisticated caching system implemented in the `denoland/deno` repository. Unlike traditional package managers that rely on a centralized `node_modules` directory, Deno uses a content-addressable cache that stores remote dependencies, compiler artifacts, and serialized V8 bytecode. This article examines the internal architecture of Deno's caching mechanism, tracing how URLs map to filesystem paths and how the runtime leverages these caches for fast module loading.

## Three-Tier Cache Architecture

Deno maintains three distinct cache types, each serving a specific purpose in the module loading pipeline. These caches are rooted in the Deno directory (`$DENO_DIR` or `$HOME/.deno`) and managed through the `DenoDir` struct in [`libs/resolver/cache/deno_dir.rs`](https://github.com/denoland/deno/blob/main/libs/resolver/cache/deno_dir.rs).

### Remote Module Cache

The **remote module cache** stores source files fetched from `http`, `https`, `wasm`, and `file` URLs. Located at `<DENO_DIR>/remote/`, this cache uses a `DiskCache` implementation to map URLs to deterministic filesystem paths. The `FileFetcher` in [`libs/resolver/file_fetcher.rs`](https://github.com/denoland/deno/blob/main/libs/resolver/file_fetcher.rs) consults this cache before making network requests; if a module exists locally, it reads directly from disk, otherwise it fetches the remote resource and writes it back to the cache.

### Generated Compiler Output

The **generated compiler output** cache stores TypeScript-to-JavaScript emitted code, source maps, and type declaration files. Residing at `<DENO_DIR>/gen/`, this cache also uses a `DiskCache` (specifically `gen_cache`). When the TypeScript compiler emits JavaScript for a `.ts` file, it checks `gen_cache` first. If the emitted file is absent or stale, the compiler generates the output and stores it for subsequent runs.

### V8 Code Cache

The **V8 code cache** stores serialized V8 bytecode for faster startup times. Unlike the file-based caches, this cache uses an SQLite-backed `CacheDB` located at `<DENO_DIR>/v8_code_cache_v2`. The `CodeCache` trait in [`runtime/code_cache.rs`](https://github.com/denoland/deno/blob/main/runtime/code_cache.rs) provides `get_sync` and `set_sync` methods that the V8 engine uses to retrieve or store compiled bytecode. This allows Deno to skip recompilation of unchanged modules on subsequent executions.

## DiskCache Implementation and URL-to-Filename Mapping

The core of Deno's file-based caching is the generic `DiskCache<TSys>` struct defined in [`libs/resolver/cache/disk_cache.rs`](https://github.com/denoland/deno/blob/main/libs/resolver/cache/disk_cache.rs). This struct provides deterministic URL-to-filename mapping that ensures the same URL always resolves to the same filesystem path.

The `get_cache_filename` method handles different URL schemes:

```rust
// src/libs/resolver/cache/disk_cache.rs
pub fn get_cache_filename(&self, url: &Url) -> Option<PathBuf> {
    // Handles http, https, data, blob via deno_cache_dir::url_to_filename
    // Handles wasm → scheme/host[/port]/path
    // Handles file → platform‑specific conversion (Windows drives, UNC, etc.)
}

```

**HTTP/HTTPS URLs** are hashed using `url_to_filename` and stored under `http/host/<hash>`, creating a content-addressable structure that prevents collisions. **Wasm URLs** use the scheme and host directly, with ports encoded as `_PORT`. **File URLs** undergo platform-specific conversion; on Windows, drive letters and UNC shares are transformed into safe path components (e.g., `file/D/a/...`).

The resulting `PathBuf` is joined with the cache's root directory (`self.location`), creating a full file path inside `<DENO_DIR>/remote` or `<DENO_DIR>/gen`.

## DenoDir and Cache Coordination

`DenoDir` in [`libs/resolver/cache/deno_dir.rs`](https://github.com/denoland/deno/blob/main/libs/resolver/cache/deno_dir.rs) serves as the central coordinator for all cache types. It owns the various `DiskCache` instances and defines the versioned folder names that ensure cache invalidation across Deno releases.

```rust
// src/libs/resolver/cache/deno_dir.rs
pub struct DenoDir<TSys: DiskCacheSys> {
    pub root: PathBuf,          // e.g. $HOME/.deno
    pub gen_cache: DiskCache<TSys>,
}

```

The struct exposes methods that return paths for the V8 code cache (`v8_code_cache_v2`) and other sub-folders. These versioned names (e.g., `v2`) are bumped when the cache format changes, automatically invalidating old caches on the next run.

`DenoDirProvider` instantiates these caches lazily—caches are created only when code first requests them. This design allows the CLI to continue operating even if the cache directory cannot be created, falling back to in-memory or temporary storage.

## CLI Integration and Cache Commands

Deno's CLI provides explicit control over caching behavior through dedicated commands and runtime flags. The `deno cache` command implementation in [`cli/tools/pm/cache_deps.rs`](https://github.com/denoland/deno/blob/main/cli/tools/pm/cache_deps.rs) forces the `FileFetcher` to download modules and populate the remote cache.

```bash

# Download and cache all imports of a script

deno cache https://deno.land/std@0.225.0/http/file_server.ts

# Run using only the cache (no network)

deno run --cached-only file_server.ts

```

Additional runtime flags modify cache behavior:

- **`--cached-only`** – Restricts the runtime to use only already-cached assets; aborts execution if any required file is missing from the remote cache.
- **`--no-remote`** – Disables network fetches entirely; the remote `DiskCache` is consulted but never written to, preventing any external network access.

Internally, the CLI constructs a `Factory` that builds a `CodeCache` from `caches.code_cache_db()` and passes it to the JavaScript runtime (see [`cli/factory.rs`](https://github.com/denoland/deno/blob/main/cli/factory.rs)). This integration ensures that V8 bytecode caching works transparently across all execution modes.

## Cache Invalidation and Versioning

Deno employs a versioning strategy to ensure cache consistency across releases. Each cache folder includes a version identifier in its path, forcing automatic invalidation when the format changes.

- **`v8_code_cache_v2`** – Bumping this version forces recompilation of all modules, ensuring compatibility with new V8 versions or bytecode format changes.
- **`fmt_incremental_cache_v2`**, **`lint_incremental_cache_v2`** – Similarly versioned to invalidate incremental caches when formatting or linting logic changes.

When the version string in `DenoDir` is updated, the next run creates fresh cache files in the new directory, leaving old versions to be garbage collected. This approach eliminates complex migration logic while ensuring that incompatible cache formats never interfere with runtime execution.

## Summary

Deno's caching mechanism provides a robust, content-addressable storage system that spans remote modules, compiler output, and V8 bytecode:

- **Three-tier architecture** separates remote module caching (`<DENO_DIR>/remote`), TypeScript compilation artifacts (`<DENO_DIR>/gen`), and V8 bytecode (`<DENO_DIR>/v8_code_cache_v2`).
- **Deterministic filename mapping** via `DiskCache::get_cache_filename` ensures URLs consistently resolve to safe filesystem paths across platforms.
- **Lazy initialization** through `DenoDirProvider` allows the runtime to function even when cache directories are unavailable.
- **Versioned invalidation** automatically refreshes caches when formats change, preventing compatibility issues across Deno releases.
- **CLI integration** provides explicit control via `deno cache`, `--cached-only`, and `--no-remote` flags.

## Frequently Asked Questions

### How does Deno handle cache storage location?

Deno stores all cache data in the Deno directory, which defaults to `$HOME/.deno` but can be overridden via the `$DENO_DIR` environment variable. Within this directory, Deno creates separate subdirectories for remote modules (`remote/`), generated compiler output (`gen/`), and V8 bytecode (`v8_code_cache_v2`), ensuring organized separation of concerns.

### What happens when Deno encounters a URL it hasn't cached before?

When Deno encounters an uncached URL, the `FileFetcher` in [`libs/resolver/file_fetcher.rs`](https://github.com/denoland/deno/blob/main/libs/resolver/file_fetcher.rs) first calls `DiskCache::get_cache_filename` to determine where the file should live. If the file doesn't exist at that path, Deno fetches the resource from the network, then writes the response to the remote cache using `DiskCache::set` before returning the content to the runtime.

### How does Deno ensure cache compatibility across version updates?

Deno embeds version identifiers directly into cache directory names, such as `v8_code_cache_v2` or `gen_cache` with implicit versioning. When the cache format changes in a new Deno release, developers increment these version strings in [`libs/resolver/cache/deno_dir.rs`](https://github.com/denoland/deno/blob/main/libs/resolver/cache/deno_dir.rs). This forces Deno to create fresh cache directories on the next run, automatically invalidating old incompatible caches without requiring manual cleanup.

### Can Deno run entirely offline using only cached modules?

Yes, Deno supports fully offline execution through the `--cached-only` runtime flag. When this flag is specified, Deno restricts the `FileFetcher` to read exclusively from the remote `DiskCache` and aborts execution with an error if any required module is missing from the cache. Additionally, the `--no-remote` flag prevents any network writes while still allowing cache reads, providing fine-grained control over network access.