Backend Storage Providers for AFFiNE: File System, S3, and Cloudflare R2 Configuration

AFFiNE supports three backend storage providers—File-System (fs), AWS S3 compatible (aws-s3), and Cloudflare R2 (cloudflare-r2)—which can be configured via the server configuration to handle blobs, attachments, and user assets.

The open-source workspace platform toeverything/AFFiNE abstracts its backend storage layer behind a unified StorageProvider interface. Understanding the available backend storage providers for AFFiNE is essential for self-hosting, as your choice determines where user uploads, document attachments, and avatar images persist.

Overview of AFFiNE Storage Providers

The storage subsystem is located in packages/backend/server/src/base/storage/ and exposes three concrete implementations registered in packages/backend/server/src/base/storage/providers/index.ts. Each provider implements the same StorageProvider interface, exposing methods like put(), get(), list(), and delete().

File-System Provider (fs)

The File-System provider stores objects as flat files on a local directory. It is the simplest option for development environments or single-node self-hosted deployments.

AWS S3 Compatible Provider (aws-s3)

The AWS S3 compatible provider works with any S3-compatible object storage, including AWS S3, MinIO, Ceph, and DigitalOcean Spaces. It supports multipart uploads, presigned URLs, and regional endpoints.

Cloudflare R2 Provider (cloudflare-r2)

The Cloudflare R2 provider extends the S3 implementation to support Cloudflare R2-specific features, including optional presigned-URL handling and account-ID configuration. R2 offers S3-compatible APIs with zero egress fees.

How to Configure Backend Storage Providers for AFFiNE

AFFiNE reads storage configuration from the server’s Config object defined in packages/backend/server/src/config.ts. The StorageProviderFactory class (packages/backend/server/src/base/storage/factory.ts) instantiates the correct provider based on the provider field.

Configuring the File-System Provider

Set the provider to fs and specify a local path. The path supports home-directory expansion using the ~ prefix.

import { StorageProviderFactory, Config } from '@affine/server';

const cfg: Config = {
  storage: {
    provider: 'fs',
    bucket: 'affine-data',
    config: { path: '/var/affine/storage' },
  },
};

const factory = new StorageProviderFactory();
const storage = factory.create(cfg.storage);

Configuring the AWS S3 Compatible Provider

Use the aws-s3 identifier for AWS S3 or compatible services like MinIO. Provide the endpoint, region, and credentials.

import { StorageProviderFactory, Config } from '@affine/server';

const cfg: Config = {
  storage: {
    provider: 'aws-s3',
    bucket: 'my-affine-bucket',
    config: {
      endpoint: 'https://s3.amazonaws.com',
      region: 'us-east-1',
      credentials: {
        accessKeyId: 'YOUR_ACCESS_KEY',
        secretAccessKey: 'YOUR_SECRET_KEY',
      },
    },
  },
};

const factory = new StorageProviderFactory();
const storage = factory.create(cfg.storage);

Configuring the Cloudflare R2 Provider

The cloudflare-r2 provider requires the account ID and supports optional presigned URL configuration for direct browser uploads.

import { StorageProviderFactory, Config } from '@affine/server';

const cfg: Config = {
  storage: {
    provider: 'cloudflare-r2',
    bucket: 'my-r2-bucket',
    config: {
      endpoint: 'https://<account>.r2.cloudflarestorage.com',
      region: 'auto',
      credentials: {
        accessKeyId: 'YOUR_R2_ACCESS_KEY',
        secretAccessKey: 'YOUR_R2_SECRET_KEY',
      },
      accountId: '<CLOUDFLARE_ACCOUNT_ID>',
      usePresignedURL: {
        enabled: true,
        urlPrefix: 'https://storage.example.com',
        signKey: 'YOUR_SIGNING_KEY',
      },
    },
  },
};

const factory = new StorageProviderFactory();
const storage = factory.create(cfg.storage);

Storage Provider Architecture and Source Files

AFFiNE uses a factory pattern to instantiate storage providers. The StorageProvider interface in packages/backend/server/src/base/storage/provider.ts defines the contract that all implementations must follow, ensuring that blob handling, attachment storage, and avatar management work transparently regardless of the backend.

Key source files implementing this architecture:

Summary

  • AFFiNE provides three backend storage providers: File-System (fs), AWS S3 compatible (aws-s3), and Cloudflare R2 (cloudflare-r2).
  • Configuration is type-safe: Set the provider field in the server Config object, and StorageProviderFactory instantiates the correct implementation.
  • All providers share the same interface: Whether storing blobs on local disk or in cloud object storage, the StorageProvider API remains consistent across packages/backend/server/src/base/storage/providers/fs.ts, s3.ts, and r2.ts.

Frequently Asked Questions

What is the default storage provider for AFFiNE?

The default provider depends on your deployment configuration. For local development, AFFiNE typically uses the File-System provider (fs) configured to store data in a local directory. Production deployments should explicitly configure either the aws-s3 or cloudflare-r2 provider in packages/backend/server/src/config.ts to ensure data persistence across container restarts.

Can I use MinIO with AFFiNE's S3 provider?

Yes. The AWS S3 compatible provider (aws-s3) works with any S3-compatible object storage, including MinIO, Ceph, and DigitalOcean Spaces. Configure the endpoint to point to your MinIO server (e.g., http://localhost:9000) and set forcePathStyle: true if required by your MinIO configuration.

Does AFFiNE support Google Cloud Storage?

While AFFiNE does not ship a dedicated Google Cloud Storage (GCS) provider, you can use GCS via the AWS S3 compatible provider (aws-s3). Google Cloud Storage offers an S3-compatible interoperability mode. Configure the endpoint to https://storage.googleapis.com and use your GCS HMAC keys for the accessKeyId and secretAccessKey.

How do I migrate data between storage providers?

AFFiNE does not currently provide an automated migration tool between storage providers. To migrate, you must manually transfer objects from the source bucket or directory to the destination using standard tools (e.g., rclone, aws s3 sync, or rsync for filesystem storage). After migration, update the server configuration in packages/backend/server/src/config.ts to point to the new provider and restart the AFFiNE server.

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 →