How to Configure an R2 Bucket as a Read-Only Mount in Cloudflare Workspace

Use the R2Bucket factory from @cloudflare/computer to create an eager mount that streams R2 objects into the workspace's virtual file system at startup, defaulting to read-only mode.

Cloudflare Computer's Mount Interface enables a Workspace to populate a subtree of its virtual file system from external data sources. The built-in R2Bucket provider creates an eager mount that lists and streams objects from an R2 bucket directly into the VFS when the workspace initializes. This guide explains how to configure a read-only R2 mount, which prevents any write operations under the mount root.

Understanding the R2Bucket Mount Provider

The R2Bucket provider lives in packages/computer/src/mounts/providers/r2.ts and implements eager materialization. According to the Cloudflare Computer source code, when mounting an R2 bucket, the provider performs three key operations at startup:

  • Lists all objects under the specified prefix using R2BucketBinding.list(), with pagination controlled by listLimit
  • Fetches each object via R2BucketBinding.get() and pipes the streaming body directly into MountWriteAPI.writeFile() without intermediate buffering
  • Sets mode: "read-only" by default, causing any fs.writeFile, fs.rm, or other mutating operations to raise EROFS

The high-level contract is documented in docs/06_mount_interface.md.

Step-by-Step Configuration

Follow these steps to configure a read-only R2 mount in your Cloudflare Workspace.

1. Import the R2Bucket Factory

Import the provider from the @cloudflare/computer package:

import { R2Bucket } from "@cloudflare/computer";

2. Declare the R2 Binding in wrangler.toml

Before using the mount, declare your R2 bucket binding in wrangler.toml:

[[r2_buckets]]
binding = "SHARED_FILES"
bucket_name = "my-bucket"

This makes the bucket available as env.SHARED_FILES in your worker.

3. Add the Mount to Workspace Options

Instantiate Workspace with a mounts configuration pointing to your R2 binding:

import { Workspace, R2Bucket } from "@cloudflare/computer";

const ws = new Workspace({
  // ... other workspace options
  mounts: {
    "/workspace/r2": R2Bucket(env.SHARED_FILES, {
      prefix: "public/",
    }),
  },
});

Key configuration points:

  • Mount root: Must be an absolute path (e.g., /workspace/r2)
  • R2 binding: Pass the binding from your Worker environment
  • prefix (optional): Scopes visible keys to a subdirectory

4. Leverage the Default Read-Only Mode

The mode option defaults to "read-only". No explicit flag is required. Attempting to write or delete files under /workspace/r2 will throw EROFS (read-only file system error).

Complete Working Example

Here's a minimal worker implementation from worker-shell/src/index.ts patterns:

import { Workspace, R2Bucket } from "@cloudflare/computer";

interface Env {
  SHARED_FILES: R2Bucket;
}

export default {
  async fetch(request: Request, env: Env) {
    const ws = new Workspace({
      mounts: {
        "/workspace/r2": R2Bucket(env.SHARED_FILES, {
          prefix: "public/",
          // mode: "read-only" is implied
        }),
      },
    });

    // Reads from /workspace/r2 serve directly from R2
    // Writes throw EROFS
    const result = await ws.run(async (fs) => {
      const content = await fs.readFile("/workspace/r2/data.json", "utf-8");
      return JSON.parse(content);
    });

    return Response.json(result);
  },
};

Key Files and References

File Purpose
packages/computer/src/mounts/providers/r2.ts R2Bucket factory implementation and eager streaming logic
docs/06_mount_interface.md Mount API documentation and read-only semantics
examples/worker-shell/README.md Concrete worker examples with R2 mounts

Summary

  • Import R2Bucket from @cloudflare/computer to create R2-backed mounts
  • Configure the mount with an absolute path, R2 binding, and optional prefix
  • Rely on defaults: Read-only mode is automatic; omit mode or set explicitly to "read-only"
  • Expect eager loading: All matching objects stream into VFS at workspace startup
  • Handle EROFS: Any write operations under the mount root will fail with read-only file system errors

Frequently Asked Questions

What happens if I try to write to a read-only R2 mount?

Any fs.writeFile, fs.rm, fs.mkdir, or similar operation under a read-only mount path throws EROFS (Error Read-Only File System). This is enforced by the VFS layer before reaching the R2 provider.

Can I switch from read-only to read-write mode?

Yes. Pass mode: "read-write" in the R2Bucket options. However, as noted in docs/06_mount_interface.md, read-write mounts require additional considerations for durability and consistency that read-only mounts avoid.

Does the R2Bucket mount cache data locally?

No. The R2Bucket provider streams object bodies directly from R2BucketBinding.get() into MountWriteAPI.writeFile() without intermediate buffering. Each object is fetched once at mount time, not on every read.

How do I limit which R2 objects appear in the mount?

Use the prefix option when calling R2Bucket(). Only keys beginning with that prefix are listed and mounted; everything else remains invisible to the workspace.

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 →