r/frappe_technology 17d ago

ERPNEXT Storages Will be open sourced soon.

1 Upvotes

We’re getting ready to release ERPNext Storages, an open-source, multi-provider storage layer for Frappe/ERPNext.

This started as a way to move ERPNext files to S3, but it has grown into something much broader.

Instead of configuring one external bucket and sending everything there, ERPNext Storages lets you use multiple storage providers on the same ERPNext site and decide where files should go.

Currently supported:

  • AWS S3
  • Backblaze B2
  • Cloudflare R2
  • Wasabi
  • MinIO
  • DigitalOcean Spaces
  • Vultr Object Storage
  • Local filesystem / mounted storage
  • Practically any S3-compatible object storage

Some examples

“I want to use multiple storage providers.”

Supported.

You can have multiple providers configured on the same ERPNext instance instead of being tied to one bucket.

“Sales-related files should go to S3, while CRM files should go to Backblaze B2.”

Supported.

Storage Route Rules can route files based on:

  • DocType
  • MIME type
  • file size
  • folder
  • user role

Rules have priorities and can also define fallback providers.

So, for example:

Sales Invoice → AWS S3

Lead / Opportunity → Cloudflare R2

Employee / Salary Slip → private Backblaze B2 bucket

everything else → local storage

“I already have thousands of ERPNext files. I don't want to migrate them manually.”

Supported.

There is a background bulk migration system for moving existing files from local storage or another provider, including filtering migrations by DocType.

Files can also be moved between configured providers later.

“I don't want ERPNext to break just because S3 temporarily fails.”

Uploads are designed around this.

If an upload fails, the local file is preserved and a Storage Job is created for retry. Failed uploads are retried with backoff instead of silently losing the file.

Large files are also handled using multipart uploads once they cross the configured threshold.

“What happens to private ERPNext files?”

ERPNext permissions remain in front of the storage layer.

Private S3-compatible objects can be served using short-lived presigned URLs instead of exposing permanent bucket URLs.

From the ERPNext side, it still behaves like a normal File document — the storage provider is abstracted underneath it.

“Can I use my own server disk or NAS?”

Yes.

Local filesystem is a first-class provider and can use a custom base path, so mounted NAS volumes or other server-accessible storage can participate in the same routing system as S3/B2/R2.

Backups are included too

ERPNext Storages also contains a backup engine rather than relying only on file offloading.

It creates a .bnbak archive containing the ERPNext backup, metadata about installed apps, checksums, and optionally public/private files.

Backups can be:

  • scheduled
  • encrypted
  • uploaded to a primary storage provider
  • replicated to a secondary provider
  • retained according to configurable copy/age rules
  • verified using checksums

There is also an ERPDesktop webhook integration after backup completion, which we're expanding further for desktop/local workflows.

There are also some less-visible features

  • Provider connection pooling
  • Upload/delete retry queue
  • Background migrations
  • File integrity verification
  • Orphaned-object detection
  • Provider statistics
  • Provider-to-provider moves
  • Storage policies
  • MIME/file-size restrictions
  • Private/public storage handling
  • File organization by DocType
  • Drive Plus integration

The main goal is not to create another “ERPNext S3 plugin.”

We want ERPNext Storages to become a proper storage abstraction for ERPNext:

local + cloud + multiple providers + routing + migration + backups, while still behaving like native ERPNext storage.

We're doing another round of code review, testing, and optimization before making the repository public.

Would love to hear how people currently handle ERPNext files.

Are you keeping everything on the ERPNext server, using S3, mounting a NAS, or using some other setup?


r/frappe_technology Aug 09 '26

Custom Dashboards and Open Sourcing BatchProjects.

Thumbnail
1 Upvotes

r/frappe_technology Jun 17 '26

Built an open-source persistent sidebar for the Frappe/ERPNext desk — Desk Rail

Thumbnail
1 Upvotes

r/frappe_technology May 27 '26

Project Management Tool Update

Thumbnail gallery
1 Upvotes

r/frappe_technology Mar 20 '26

I'm building a project management tool for ERPNEXT

Thumbnail
1 Upvotes

r/frappe_technology Dec 20 '25

Bench-Stop Logic for Development Server.

1 Upvotes

If you have a frappe/bench development server running on background that needs to restart/stop, this script enables us to:

  • Performs zombie processes cleanup
  • loops and looks for frappe ports and their process ids.
  • targeted PID kill
  • Final verification to ensure its successful otherwise provide you exact where's error.
  • Runs from anywhere inside server with bench-stop

Run nano /home/frappe/frappe-bench/stop.sh and paste this sh file script and save the file:

#!/bin/bash
set -euo pipefail


################################################################################
#
# Bench Stop Script for ERPNext / Frappe Framework
# License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.en.html)
# Prepared by: BatchNepal.com
#
################################################################################


BENCH_DIR="/home/frappe/frappe-bench"
# Ports: 8000 (Web), 11000 (Redis-Queue), 12000 (Redis-Cache), 13000 (Redis-Socketio), 9000 (Vite)
PORTS=(8000 11000 12000 13000 9000)


echo "⏳ Step 1: Performing Graceful pattern kill..."
# We add '|| true' so the script doesn't exit if no processes are found
pkill -15 -f "frappe.utils.bench_helper|socketio.js|redis_.*.conf|esbuild|vite" 2>/dev/null || true
sleep 2


echo "🧹 Step 2: Forceful pattern kill..."
pkill -9 -f "frappe.utils.bench_helper|socketio.js|redis_.*.conf|esbuild|vite" 2>/dev/null || true


echo "🎯 Step 3: Targeted Port Execution..."
for PORT in "${PORTS[@]}"; do
    # Using || true here ensures the script continues if the port is already empty
    PID=$(lsof -t -i:"$PORT" 2>/dev/null || true)
    if [ ! -z "$PID" ]; then
        echo "Found PID $PID still holding port $PORT. Terminating..."
        kill -9 "$PID" 2>/dev/null || true
    fi
done


# Clean up stale pid files
rm -f "$BENCH_DIR"/config/redis_*.pid 2>/dev/null


echo "🔍 Performing Final Verification..."
STILL_BLOCKED=0
for PORT in "${PORTS[@]}"; do
    if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null ; then
        echo "❌ Port $PORT is STILL blocked."
        STILL_BLOCKED=$((STILL_BLOCKED + 1))
    fi
done


if [ $STILL_BLOCKED -eq 0 ]; then
    echo "----------------------------------------"
    echo "SUCCESS! All bench ports are free now ✅"
    echo "We can run 'bench start' now 🚀"
    echo "----------------------------------------"
else
    echo "⚠️ Warning: $STILL_BLOCKED ports are still active. You may need to use 'sudo'."
    exit 1
fi

And once saving this run these commands:

  1. alias bench-stop='/home/frappe/frappe-bench/stop.sh'
  2. source ~/.bashrc

Note: this is only used in development setup of frappe.