/storage_letter.

Unified Filesystem Mount Across AWS GCS R2 and Azure Blob From a Single Configuration File

One mount translates filesystem calls across AWS, Google, Azure, and Cloudflare buckets.

Sponsored by Archil

Storage Letter Editors
· 8 min read
Cover illustration for “Unified Filesystem Mount Across AWS GCS R2 and Azure Blob From a Single Configuration File”
Multi-Cloud Storage · October 21, 2026 · 8 min read · 1,800 words

A unified filesystem mount lets applications access existing AWS S3, GCS, R2, and Azure Blob buckets through the same filesystem path, without migrating objects or rewriting storage logic for each provider. One configuration file sets up routing and authentication behind that interface. The simpler integration leaves differences in storage behavior intact, so credentials, concurrency, and isolation still need provider-specific care.

Why every object store demands its own integration, and what that costs

Mountpoint for Amazon S3 gives S3 a filesystem interface, but it brings its own mount configuration and authentication requirements. Google's gcsfuse does the same for GCS through a separate driver and configuration. Microsoft's blobfuse2 adds another set for Azure Blob. R2 offers an S3-compatible interface, but compatibility does not make its behavior identical to AWS S3. A multi-cloud deployment therefore inherits several integration jobs for what the application considers one task, namely opening a file.

That duplication reaches deeper than deployment scripts. An application written directly against S3 must rework its storage logic to read from GCS or Azure Blob. The application has to handle each provider's authentication, requests, and failures. Separate FUSE drivers can do some of that work for applications, but teams still have three mount configurations to keep up. The multiplicity tax arrives in small invoices, all addressed to engineering.

How can an existing bucket work like a local folder without migration?

A unified FUSE mount places one translation layer between the application and the object stores. The application sees a normal directory and issues filesystem operations, while the mount translates them into provider-specific API calls. Schemes including s3://, gs://, and azblob:// select the backend, and the layer handles authentication and wire protocols. The application's path and file-handling code can stay the same when the backing provider changes. The objects remain in the existing bucket, eliminating the need for ETL.

The translator must handle differences in authentication models, consistency guarantees, multipart-upload limits, and support for features such as generation numbers across backends. S3-compatible storage can also differ in ACL defaults and conditional-write support. Treating that label as full equivalence invites bugs under concurrent writes.

FUSE itself guarantees only filesystem access, leaving local-disk performance and full POSIX semantics outside that guarantee. Cloudflare's R2 FUSE mount documentation states that limit explicitly and identifies datasets, models, and shared assets as useful read workloads. Full POSIX semantics over object storage, or an S3-compatible API that stays consistent with the mounted view, are properties of the storage layer behind a mount, not something the word FUSE guarantees.

The two families of unified abstraction — FUSE mounts vs. library layers

The main choice depends on who consumes the storage interface. FUSE mounts work for processes that open files, including existing tools and legacy applications written in any language. They leave the application unchanged but still have FUSE overhead and the limits on filesystem semantics described above. Unified libraries work for code that can use a common storage API. They keep the kernel out of the work and have lower overhead, but the application has to change.

Apache OpenDAL represents the library approach through a unified Rust core that constructs HTTP requests and parses responses for each service. Its shared implementation enables retry and logging behavior across backends, and its oli CLI handles cross-cloud object operations in place of tools such as s3cmd, azcopy, and gsutil. Its experimental ofs tool provides a FUSE filesystem in place of tools such as s3fs and gcsfuse. The same core can therefore serve either an API consumer or a process expecting files.

Beyond those two families, storage can be built into the execution layer itself. In that model, the sandbox platform mounts the buckets into the environment before the agent's code runs, so agents use them without storage logic for each provider. The distinction is where the abstraction lives, whether beneath an existing process, inside application code, or within the environment running that code.

How mntrs and OpenDAL handle the single-config-file pattern in practice

mntrs, a young open-source project, is a Rust FUSE mount backed by Apache OpenDAL that supports 13 storage backends through one mount command or configuration file. Its shared --opt interface accepts options including endpoint, access-key, secret-key, and region, with TLS options named cacert, cert, and key. Keys for each backend add to those names while keeping the same configuration vocabulary.

For mntrs, the s3:// scheme covers AWS S3, MinIO, R2, and Ceph, with AKID/SK or IAM authentication. Google Cloud Storage uses gs:// or gcs:// with a service account, while Azure Blob uses azblob:// with a connection string or SAS. OpenDAL's abbreviated URI syntax makes the routing visible in expressions such as s3://bucket?region=us-east-1 and azblob://container?account_name=myaccount. Those examples show the core library's URI-based configuration, whereas oli uses a TOML profile configuration file.

mntrs also shares a caching, prefetching, and write-back pipeline across backends. Its single-file write cache supports WriteAt random writes, its adaptive prefetcher doubles chunk size on sequential reads, and its write-back queue provides fsync semantics with a .dirty sidecar for crash recovery. CRC64 protects disk-cache integrity. These features give the mount common machinery without making backend guarantees interchangeable. For a data-pipeline alternative, dlt's filesystem destination uses fsspec and a single secrets.toml block, with s3://, gs://, or az:// selecting AWS S3, Google Cloud Storage, or Azure Blob Storage.

Where credentials actually live when a unified mount is in play

Centralizing configuration can also centralize exposure. If one file contains credentials for every provider, a breach of that file can expose every connected bucket. One configuration file is useful. One treasure chest of permanent secrets is rather less charming.

Environment-variable injection is the minimum mitigation because it removes secrets from the configuration file. However, those secrets still enter the process environment and remain readable by code running in the same process space. A stronger pattern uses brokered credentials that stay outside the sandbox or container. The process requests a short-lived, narrowly scoped token per call, and the broker mints it against the originating user's permissions. No ambient credential then sits in the mount environment.

Cloud-hosted sandboxes also need a separate network control against instance metadata access. Blocking 169.254.169.254 prevents an agent from reaching the IMDS through that endpoint to obtain instance credentials or tokens. Moving bucket secrets out of a file does not close this other route. Credential placement and metadata access address different paths to the same data, so neither substitutes for the other.

How isolation and egress policy interact with multi-bucket mounts in agent workloads

Agent-generated code changes the threat model because the code consuming the mount may itself be hostile. A compromised agent with access to several providers can expose data from all of them. If the sandbox can reach the open internet, injected code can POST bucket contents to an attacker-controlled host regardless of how carefully the mount stores credentials. Default-deny egress turns that route from trivial into blocked and logged. Per-provider credential scoping further limits which data the compromised workload can reach.

Firecracker microVMs provide a hardware-virtualized guest kernel, separating the guest environment from the host and other tenants' mounts. gVisor uses its user-space kernel, Sentry, to intercept container syscalls, placing that process between the workload and the host kernel. Plain containers instead share the host kernel, so a kernel vulnerability can expose the host and resources reachable from it.

As more buckets join a mount, the potential blast radius grows with them. A mount spanning S3, GCS, and Azure Blob does not gain safety merely by having fewer configuration files. Its egress rules and credential scopes matter more because one execution environment can now reach more data.

Persistence, branching, and concurrent access across providers from a shared mount

Once access is contained, persistence makes the mount more than a convenient adapter. Ephemeral sandboxes lose intermediate work and require package reinstallation after restarts. A persistent disk preserves that state across stops and restarts, allowing an agent to resume work in its existing workspace. An S3-compatible API adds another way to reach the same disk's data. Together, those capabilities separate the life of the work from the life of the process doing it.

Branching raises the ceiling further by supporting parallel attempts from a shared starting point. In S3, R2, and standard object storage, creating a branch requires physically copying objects into a new bucket or prefix. That makes parallel experiments costly and slow to set up and remove. A layer that supports fast branching changes that constraint, but unified mounting alone does not establish that it offers such a feature.

Concurrent access also needs a separate check because several sandboxes opening one mount need protection against corruption. Persistence, branching, and safe shared access vary by provider and abstraction layer. A single configuration file simplifies adding a provider, but it does not answer the concurrency question for any of them.

Per-tenant storage isolation when many agents each have their own mount

At multi-tenant scale, “any bucket” must still mean “only your bucket.” When application logic keeps tenants apart by path, one misconfigured path or storage-layer bug can expose another tenant's data. Workspace-per-tenant isolation moves the boundary into storage itself. Each tenant gets its own bucket or volume and its own credentials, attached only to that tenant's sandboxes. Another tenant's paths must remain unreachable regardless of application logic.

When the configuration covers several providers, audits of the application alone have a harder time proving that boundary holds. The guarantee must come from the infrastructure, including the hypervisor and mount configuration. A microVM per tenant, with its own kernel and only that tenant's storage credentials, is one way to get that guarantee.

Economics and residency also become storage-level concerns when thousands of tenants each have a mount. A tenant using 1 MB and one using 1 GB need different amounts of storage, so metering active data avoids counting their needs as the same amount of provisioned capacity. Where any cache layer sits also matters when tenants have buckets across providers and regions, each with its own residency requirements.

What to verify before committing a unified mount to production

Before production, verify concurrent writes, cold-tier retrieval latency, and consistency behavior for each backend. Conditional-write support differs between providers, so check each one's documentation before relying on it. Amazon S3 and Google Cloud Storage document strong read-after-write consistency, but other S3-compatible stores vary, and a mount's own cache can serve stale data whatever the backend.

Check the filesystem semantics the workload actually requires. s3fs-fuse explicitly documents no atomic renames of files or directories, no hard links, and no coordination between multiple clients mounting the same bucket. Check the minimum credential permissions each bucket needs.

Before connecting production credentials, check that default-deny egress is in place and that the network blocks 169.254.169.254. Then name the isolation mechanism and its kernel-sharing model. Firecracker, gVisor, and a named container runtime describe checkable boundaries, while “secure sandbox” does not.

This article is sponsored by Archil.

Sources

  1. Object store & filesystem
  2. Mount R2 buckets with FUSE · Cloudflare Containers docs

More in Multi-Cloud Storage