Frappe R2 Storage
Store Frappe / ERPNext files in Cloudflare R2 buckets. Assign an R2 bucket per Frappe folder (or "Home" for all files) and files are uploaded, streamed, moved and deleted transparently.
- Author: devsohanur653-lab
- Repository: https://github.com/devsohanur653-lab/frappe_r2_storage
- GitHub stars: 3
- Forks: 0
- License: NOASSERTION
- Category: Integrations
- Maintenance: Actively Maintained
Install Frappe R2 Storage
bench get-app https://github.com/devsohanur653-lab/frappe_r2_storage
Add the Frappe Gems badge to your README
Maintain Frappe R2 Storage? Paste this into your README:
[](https://frappegems.com/gems/apps/devsohanur653-lab/frappe_r2_storage)
About Frappe R2 Storage
R2 Storage
Store Frappe / ERPNext files in Cloudflare R2 buckets. Assign an R2 bucket per Frappe folder (or "Home" for all files) and files are uploaded, streamed, moved and deleted transparently.
R2 Storage is a fork of DFP External Storage by Development for People (MIT), focused on Cloudflare R2.
What is different from DFP External Storage
- Account ID based setup: enter your Cloudflare Account ID and the R2 endpoint (
.r2.cloudflarestorage.com) is built for you. TLS and regionautoare set automatically. - Jurisdictions: Default, EU (
.eu.r2.cloudflarestorage.com) and FedRAMP endpoints. - Public bucket URL: set your
https://pub-xxxx.r2.devURL or custom domain and public files are redirected straight to Cloudflare (no Frappe proxy, CDN cached). Private files always go through Frappe permission checks. - R2 environment variables:
R2_ACCESS_KEY_ID/R2_SECRET_ACCESS_KEYare read when keys are left empty (AWS / MinIO variables still work as fallback). - Any other S3 compatible service can still be used by choosing type "S3 Compatible" and filling
host:port.
Requirements
- Frappe version 15
- A Cloudflare account with R2 enabled
Functionalities
- R2 bucket per folder(s). If "Home" folder is selected, all Frappe / ERPNext files use that bucket.
- Files accessible with URLs:
/file/[File ID]/[file name.extension] - Frappe private/public permissions preserved for remote files.
- Storages can be write disabled; existing files stay readable.
- Bulk file relocation between buckets or to/from local filesystem, streamed in chunks (no full file in memory).
- Same file (same hash) reuses the existing object key.
- Bucket file listing page.
- Storage can not be deleted while files use it.
- If the bucket is not reachable on upload, the file is kept on local filesystem.
- Presigned URLs (optionally by mimetype, e.g.
video/for streaming). - Redis cache for small public files, configurable stream buffer size, remote file size for files > 2GB.
Cloudflare R2 setup
- Cloudflare dashboard → R2 → Create bucket (keep it private).
- R2 → Manage R2 API Tokens → Create API token, permission Object Read & Write, scoped to the bucket.
- Copy Account ID, Access Key ID and Secret Access Key.
- Optional: bucket → Settings → enable Public Development URL or connect a Custom Domain if you want public files served directly by Cloudflare.
Install
cd ~/frappe-bench
bench get-app https://github.com/devsohanur653-lab/frappe_r2_storage.git
bench --site your-site.localhost install-app r2_storage
bench --site your-site.localhost migrate
> Do not install r2_storage and dfp_external_storage on the same site: both override the File DocType.
Configure
Desk → R2 Storage → New:
| Field | Value |
|---|---|
| Type | Cloudflare R2 |
| Cloudflare Account ID | your account ID |
| Bucket name | your bucket |
| Access Key / Secret Key | from the R2 API token |
| Folder(s) | Home for all files, or specific folders |
| Public bucket URL | optional, https://pub-xxxx.r2.dev or custom domain |
On save the connection is checked ("Great! Bucket is accesible").
Flow options
- No storage assigned to a folder: files stay on the local filesystem.
- Storage assigned to "Attachments": only files in that folder go to R2.
- Storage assigned to "Home": all files go to R2 (except folders with their own storage).
File actions
- File with an R2 Storage, changed to another one: streamed from old bucket to new bucket, then deleted from the old one.
- File with an R2 Storage, cleared: downloaded to local filesystem, then deleted from the bucket.
- Local file, R2 Storage assigned: uploaded to the bucket, then deleted from the filesystem.
Python API
Upload from a local file:
file_doc = frappe.get_doc({
"doctype": "File",
"is_private": True,
"file_name": "file name here"
})
file_doc.r2_storage_upload_file(filepath)
file_doc.save()
Download to a local file:
file_doc = frappe.get_doc("File", doc_name)
file_doc.r2_storage_download_to_file("/path/to/local/file")
Read a remote file through a proxy object (only requested bytes are fetched):
file_doc = frappe.get_doc("File", doc_name)
with zipfile.ZipFile(file_doc.r2_storage_file_proxy()) as z:
for zipinfo in z.infolist():
print(zipinfo.filename)
Uninstall
bench --site {site} uninstall-app r2_storage --no-backup
bench remove-app r2_storage
Attributions
- Based on DFP External Storage by Development for People (MIT).
- Stream support originally by Khoran.
- Cloud storage icon by Iconstock from Noun Project.
License
MIT. See LICENSE.md.
Related Integrations apps for Frappe & ERPNext
- Insights — Open Source Business Intelligence Tool
- Raven — Simple, open source team messaging platform
- Frappe Whatsapp — WhatsApp cloud integration for frappe
- Frappe Assistant Core — Infrastructure that connects LLMs to ERPNext. Frappe Assistant Core works with the Model Context Protocol (MCP) to expose ERPNext functionality to any compatible Language Model
- Biometric Attendance Sync Tool — A simple tool for syncing Biometric Attendance data with your ERPNext server
- Frappe React Sdk — React hooks for Frappe
- Frappe Js Sdk — TypeScript/JavaScript library for Frappe REST API
- Mcp — Frappe MCP allows Frappe apps to function as MCP servers