Mount R2 buckets with FUSE
Mount R2 buckets as filesystems using FUSE in Containers
FUSE (Filesystem in Userspace) allows you to mount R2 buckets as filesystems within Containers. Applications can then interact with R2 using standard filesystem operations rather than object storage APIs.
Common use cases include:
- Bootstrapping containers with assets - Mount datasets, models, or dependencies for sandboxes and agent environments
- Persisting user state - Store and access user configuration or application state without managing downloads
- Large static files - Avoid bloating container images or downloading files at startup
- Editing files - Make code or config available within the container and save edits across instances.
To mount an R2 bucket, install a FUSE adapter in your Dockerfile and configure it to run at container startup.
This example uses tigrisfs ↗, which supports S3-compatible storage including R2:
Dockerfile
FROM alpine:3.20
# Install FUSE and dependenciesRUN apk update && \ apk add --no-cache ca-certificates fuse curl bash
# Install tigrisfsRUN ARCH=$(uname -m) && \ if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi && \ if [ "$ARCH" = "aarch64" ]; then ARCH="arm64"; fi && \ VERSION=$(curl -s https://api.github.com/repos/tigrisdata/tigrisfs/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4) && \ curl -L "https://github.com/tigrisdata/tigrisfs/releases/download/${VERSION}/tigrisfs_${VERSION#v}_linux_${ARCH}.tar.gz" -o /tmp/tigrisfs.tar.gz && \ tar -xzf /tmp/tigrisfs.tar.gz -C /usr/local/bin/ && \ rm /tmp/tigrisfs.tar.gz && \ chmod +x /usr/local/bin/tigrisfs
# Create startup script that mounts bucket and runs a commandRUN printf '#!/bin/sh\n\ set -e\n\ \n\ mkdir -p /mnt/r2\n\ \n\ R2_ENDPOINT="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"\n\ echo "Mounting bucket ${BUCKET_NAME}..."\n\ /usr/local/bin/tigrisfs --endpoint "${R2_ENDPOINT}" -f "${BUCKET_NAME}" /mnt/r2 &\n\ sleep 3\n\ \n\ echo "Contents of mounted bucket:"\n\ ls -lah /mnt/r2\n\ ' > /startup.sh && chmod +x /startup.sh
CMD ["/startup.sh"]The startup script creates a mount point, starts tigrisfs in the background to mount the bucket, and then lists the mounted directory contents.
Your Container needs R2 credentials and configuration passed as environment variables. Store credentials as Worker secrets, then pass them through the envVars property:
import { Container, getContainer } from "@cloudflare/containers";
export class FUSEDemo extends Container { defaultPort = 8080; sleepAfter = "10m"; envVars = { AWS_ACCESS_KEY_ID: this.env.AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: this.env.AWS_SECRET_ACCESS_KEY, BUCKET_NAME: this.env.R2_BUCKET_NAME, R2_ACCOUNT_ID: this.env.R2_ACCOUNT_ID, };}import { Container, getContainer } from "@cloudflare/containers";
interface Env { FUSEDemo: DurableObjectNamespace<FUSEDemo>; AWS_ACCESS_KEY_ID: string; AWS_SECRET_ACCESS_KEY: string; R2_BUCKET_NAME: string; R2_ACCOUNT_ID: string;}
export class FUSEDemo extends Container<Env> { defaultPort = 8080; sleepAfter = "10m"; envVars = { AWS_ACCESS_KEY_ID: this.env.AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: this.env.AWS_SECRET_ACCESS_KEY, BUCKET_NAME: this.env.R2_BUCKET_NAME, R2_ACCOUNT_ID: this.env.R2_ACCOUNT_ID, };}The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY should be stored as secrets, while R2_BUCKET_NAME and R2_ACCOUNT_ID can be configured as variables in your wrangler.jsonc:
{ "vars": { "R2_BUCKET_NAME": "my-bucket", "R2_ACCOUNT_ID": "your-account-id" }}Other S3-compatible storage providers, including AWS S3 and Google Cloud Storage, can be mounted using the same approach as R2. You will need to provide the appropriate endpoint URL and access credentials for the storage provider.
To mount a specific prefix (subdirectory) within a bucket, most FUSE adapters require mounting the entire bucket and then accessing the prefix path within the mount.
With tigrisfs, mount the bucket and access the prefix via the filesystem path:
RUN printf '#!/bin/sh\n\ set -e\n\ \n\ mkdir -p /mnt/r2\n\ \n\ R2_ENDPOINT="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"\n\ /usr/local/bin/tigrisfs --endpoint "${R2_ENDPOINT}" -f "${BUCKET_NAME}" /mnt/r2 &\n\ sleep 3\n\ \n\ echo "Accessing prefix: ${BUCKET_PREFIX}"\n\ ls -lah "/mnt/r2/${BUCKET_PREFIX}"\n\ ' > /startup.sh && chmod +x /startup.shYour application can then read from /mnt/r2/${BUCKET_PREFIX} to access only the files under that prefix. Pass BUCKET_PREFIX as an environment variable alongside your other R2 configuration.
To prevent applications from writing to the mounted bucket, add the -o ro flag to mount the filesystem as read-only:
RUN printf '#!/bin/sh\n\ set -e\n\ \n\ mkdir -p /mnt/r2\n\ \n\ R2_ENDPOINT="https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"\n\ /usr/local/bin/tigrisfs --endpoint "${R2_ENDPOINT}" -o ro -f "${BUCKET_NAME}" /mnt/r2 &\n\ sleep 3\n\ \n\ ls -lah /mnt/r2\n\ ' > /startup.sh && chmod +x /startup.shThis is useful for shared assets or configuration files where you want to ensure applications only read data.
- Container environment variables - Learn how to pass secrets and variables to Containers
- tigrisfs ↗ - FUSE adapter for S3-compatible storage including R2
- s3fs ↗ - Alternative FUSE adapter for S3-compatible storage
- gcsfuse ↗ - FUSE adapter for Google Cloud Storage buckets
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-