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

> Learn to configure an R2 bucket as a read-only mount in Cloudflare Workspace using the R2Bucket factory. Stream R2 objects into your virtual file system at startup for easy access.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: how-to-guide
- Published: 2026-08-16

---

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

```typescript
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`](https://github.com/cloudflare/computer/blob/main/wrangler.toml):

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

```typescript
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`](https://github.com/cloudflare/computer/blob/main/worker-shell/src/index.ts) patterns:

```typescript
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`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/mounts/providers/r2.ts) | `R2Bucket` factory implementation and eager streaming logic |
| [`docs/06_mount_interface.md`](https://github.com/cloudflare/computer/blob/main/docs/06_mount_interface.md) | Mount API documentation and read-only semantics |
| [`examples/worker-shell/README.md`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/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.