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.
- Identifier:
fs - Source:
packages/backend/server/src/base/storage/providers/fs.ts - Best for: Local development, small deployments, or NFS-mounted volumes
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.
- Identifier:
aws-s3 - Source:
packages/backend/server/src/base/storage/providers/s3.ts - Best for: Production deployments requiring scalable object storage
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.
- Identifier:
cloudflare-r2 - Source:
packages/backend/server/src/base/storage/providers/r2.ts - Best for: Cost-sensitive deployments requiring global CDN integration
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:
packages/backend/server/src/base/storage/providers/index.ts– Registers the three provider classes and exports theStorageProvidersmap andStorageProviderConfigtype.packages/backend/server/src/base/storage/providers/fs.ts–FsStorageProviderimplementation for local filesystem storage.packages/backend/server/src/base/storage/providers/s3.ts–S3StorageProviderimplementation supporting any S3-compatible API.packages/backend/server/src/base/storage/providers/r2.ts–R2StorageProviderextending S3 with Cloudflare-specific optimizations.packages/backend/server/src/base/storage/factory.ts–StorageProviderFactorythat creates provider instances based on configuration.packages/backend/server/src/base/storage/provider.ts– Base interface definingput,get,list,delete, and other storage operations.
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
providerfield in the serverConfigobject, andStorageProviderFactoryinstantiates the correct implementation. - All providers share the same interface: Whether storing blobs on local disk or in cloud object storage, the
StorageProviderAPI remains consistent acrosspackages/backend/server/src/base/storage/providers/fs.ts,s3.ts, andr2.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →