Skip to main content

How to Self-Host ERPNext: Open Source ERP Alternative 2026

·OSSAlt Team
erpnexterpself-hostingdockerfrappesap-alternativeodoo-alternativeopen-source
Share:

How to Self-Host ERPNext: Open Source ERP Alternative in 2026

TL;DR

SAP starts at $99/user/month with a 4-9 month implementation costing $100,000-$500,000. Odoo charges $24.90/user/month for its commercial modules. ERPNext is the open source alternative — GPL v3 license, no "Enterprise Edition" paywall, and the exact same version used by enterprise clients available for free on GitHub. Version 16 (released December 2025) brings 2x faster performance and a redesigned workspace UI. For 50 users, ERPNext on a self-hosted VPS costs ~$50/month in infrastructure vs $1,245/month for Odoo Enterprise or $5,000+/month for SAP. This guide walks you through a production-ready ERPNext v16 deployment with Docker Compose.

Key Takeaways

  • No Enterprise Edition paywall: ERPNext has zero closed-source modules — the GitHub version is the enterprise version
  • 22,000+ GitHub stars: The most-starred open source ERP project ever built
  • Version 16 (December 2025): 2x performance improvements, redesigned workspaces, enhanced analytics
  • Full module suite: Accounting, HR/Payroll, Inventory, Manufacturing, CRM, Projects, and Website — all included
  • GPL v3 license: Full source access; Frappe Cloud offers managed hosting from $5/month if you don't want to self-host
  • Odoo comparison: ERPNext is fully open source with no module paywalls; Odoo's most useful modules are closed-source Community Edition
  • SAP comparison: ERPNext costs 99% less to implement and 95% less per user — with comparable coverage for SMB use cases

Why Teams Choose ERPNext Over Commercial ERPs

ERP is where the enterprise software pricing trap is most extreme. SAP and Oracle NetSuite treat implementation cost as a feature — a $200,000 deployment creates switching costs that lock customers in for decades.

ERPNext breaks this model deliberately. Frappe (the company behind ERPNext) explicitly rejects "Open Core" pricing — there are no features behind a commercial license, no modules that only enterprise clients get, no artificial tiers. The version you download from GitHub is identical to what paying Frappe Cloud customers use.

The trade-offs are real. SAP has 50+ years of industry-specific vertical functionality, a massive partner ecosystem, and certifiable compliance certifications for every major jurisdiction. ERPNext covers the 80% of ERP use cases that most SMBs actually need — accounting, HR, inventory, manufacturing, and CRM — without the implementation overhead or the annual license cost that grows with your headcount.

For a 50-person company choosing their first ERP, ERPNext self-hosted saves roughly $1,200/month vs Odoo Enterprise and $5,000+/month vs SAP Business One.

ERPNext (Self-Hosted)Odoo EnterpriseSAP Business One
LicenseGPL v3 (free)Proprietary (SaaS)Proprietary
10 users/month~$12 (VPS)$249/mo$990/mo
50 users/month~$50 (VPS)$1,245/mo~$4,950/mo
100 users/month~$100 (VPS)$2,490/mo~$9,900/mo
Implementation cost$0–$30K$10K–$80K$100K–$500K
Open source✅ Full❌ Core only
Multi-company✅ (paid)
Manufacturing/MRP✅ (paid add-on)
Payroll✅ (paid add-on)
eCommerce✅ Built-in✅ (paid add-on)

System Requirements

ERPNext v16 runs a full Frappe stack — Python app server, Node.js frontend builder, Redis queues, and MariaDB. It's not lightweight:

  • CPU: 2 vCPU minimum (4 vCPU recommended for 10+ concurrent users)
  • RAM: 4GB minimum (8GB recommended for production)
  • Storage: 40GB SSD minimum (100GB recommended — attachments and logs grow)
  • OS: Ubuntu 22.04+ or Debian 12+ (Docker-compatible Linux)
  • Docker: v24.0+
  • Docker Compose: v2.x plugin
  • Ports: 80 and 443 for web UI
  • Domain: Required for production HTTPS

Frappe stack components:

  • Python 3.11+ (app framework)
  • Node.js 18+ (frontend compilation)
  • Redis 5+ (queue, cache, socketio)
  • MariaDB 10.6+ or PostgreSQL 13+
  • wkhtmltopdf (PDF generation for invoices, reports)

Recommended servers (2026 pricing):

  • Hetzner CX32: 4 vCPU, 8GB RAM, €8.70/month — up to 25 users
  • Hetzner CX42: 8 vCPU, 16GB RAM, €19.60/month — 25-100 users

Architecture Overview

ERPNext has more moving parts than most self-hosted apps. Understanding the components prevents confusion when something goes wrong:

┌──────────────────────────────────────────────────────┐
│                  ERPNext Stack                       │
│                                                      │
│  ┌──────────┐    ┌──────────┐    ┌───────────────┐  │
│  │  Nginx   │───▶│ Backend  │───▶│   MariaDB     │  │
│  │ Frontend │    │ (Python/ │    │   (database)  │  │
│  │          │    │  Frappe) │    └───────────────┘  │
│  └──────────┘    └────┬─────┘                       │
│                       │         ┌───────────────┐   │
│                       ├────────▶│  Redis Queue  │   │
│                       │         │  (background  │   │
│                       │         │   jobs)       │   │
│  ┌──────────┐         │         └───────────────┘   │
│  │ Workers  │◀────────┤                             │
│  │ (queue   │         │         ┌───────────────┐   │
│  │ workers) │         └────────▶│  Redis Cache  │   │
│  └──────────┘                   └───────────────┘   │
│                                                      │
│  ┌──────────┐                   ┌───────────────┐   │
│  │Scheduler │                   │  SocketIO     │   │
│  │(cron)    │                   │  (real-time   │   │
│  └──────────┘                   │   updates)    │   │
│                                 └───────────────┘   │
└──────────────────────────────────────────────────────┘

Self-Hosting with Docker Compose

Frappe maintains the official frappe_docker repository. The recommended production setup uses their compose files with a few customizations.

Step 1: Clone frappe_docker

git clone https://github.com/frappe/frappe_docker.git ~/erpnext
cd ~/erpnext

Step 2: Create Your Environment File

# .env — do not commit this file
DB_PASSWORD=changeme-strong-db-password
ERPNEXT_VERSION=v16
FRAPPE_VERSION=v16

Step 3: Create docker-compose.yml

The official frappe_docker repo ships example compose files. Here's a production-ready setup for ERPNext v16:

# docker-compose.yml
version: "3.8"

x-erpnext: &erpnext
  image: frappe/erpnext:${ERPNEXT_VERSION:-v16}
  restart: unless-stopped
  volumes:
    - sites:/home/frappe/frappe-bench/sites
    - logs:/home/frappe/frappe-bench/logs

services:
  configurator:
    <<: *erpnext
    command: >
      bash -c "bench set-config -g db_host db
      && bench set-config -gp db_port 3306
      && bench set-config -g redis_cache redis://redis-cache:6379
      && bench set-config -g redis_queue redis://redis-queue:6379
      && bench set-config -g redis_socketio redis://redis-socketio:6379
      && bench set-config -gp socketio_port 9000"
    depends_on:
      db:
        condition: service_healthy
      redis-cache:
        condition: service_started
      redis-queue:
        condition: service_started
      redis-socketio:
        condition: service_started
    restart: "no"

  create-site:
    <<: *erpnext
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs
    environment:
      DB_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      SITE_NAME: erp.yourdomain.com
      INSTALL_APPS: erpnext
    command: >
      bash -c "wait-for-it -t 120 db:3306
      && wait-for-it -t 120 redis-cache:6379
      && wait-for-it -t 120 redis-queue:6379
      && wait-for-it -t 120 redis-socketio:6379
      && export start=`date +%s`
      && until [[ -n `grep -hs ^ sites/common_site_config.json | python -c \"import sys; import json; config = json.load(sys.stdin); print(config.get('db_host', ''))\"` ]]; do
      && echo \"Waiting for Frappe configurator...\";
      && sleep 1; now=`date +%s`; elapsed=$((now-start));
      && if [[ $elapsed -gt 120 ]]; then exit 1; fi; done
      && echo sites/assets >/etc/rsyslog.d/ignore.conf
      && bench new-site --no-mariadb-socket --admin-password=changeme --db-root-username=root --db-root-password=${DB_PASSWORD} --install-app erpnext --set-default erp.yourdomain.com"
    depends_on:
      configurator:
        condition: service_completed_successfully
    restart: "no"

  backend:
    <<: *erpnext
    depends_on:
      create-site:
        condition: service_completed_successfully

  frontend:
    <<: *erpnext
    command: nginx-entrypoint.sh
    environment:
      BACKEND: backend:8000
      SOCKETIO: websocket:9000
      UPSTREAM_REAL_IP_ADDRESS: "127.0.0.1"
      FRAPPE_SITE_NAME_HEADER: erp.yourdomain.com
    ports:
      - "80:8080"
    depends_on:
      backend:
        condition: service_started

  websocket:
    <<: *erpnext
    command: ["node", "/home/frappe/frappe-bench/apps/frappe/socketio.js"]
    depends_on:
      backend:
        condition: service_started

  queue-short:
    <<: *erpnext
    command: bench worker --queue short
    depends_on:
      backend:
        condition: service_started

  queue-long:
    <<: *erpnext
    command: bench worker --queue long
    depends_on:
      backend:
        condition: service_started

  scheduler:
    <<: *erpnext
    command: bench schedule
    depends_on:
      backend:
        condition: service_started

  db:
    image: mariadb:10.6
    healthcheck:
      test: mysqladmin ping -h localhost --password=${DB_PASSWORD}
      interval: 10s
      timeout: 5s
      retries: 10
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake
      - --skip-innodb-read-only-compressed
    restart: unless-stopped

  redis-cache:
    image: redis:6.2-alpine
    restart: unless-stopped

  redis-queue:
    image: redis:6.2-alpine
    restart: unless-stopped

  redis-socketio:
    image: redis:6.2-alpine
    restart: unless-stopped

volumes:
  db-data:
  sites:
  logs:

Step 4: Launch ERPNext

# First run — this creates the site and installs ERPNext
# Takes 10-15 minutes on first boot
docker compose up -d

# Monitor progress
docker compose logs -f create-site

Look for Site erp.yourdomain.com installed successfully in the logs.

Step 5: Access ERPNext

Open http://your-server-ip and log in with:

  • Username: Administrator
  • Password: changeme (from the create-site command — change immediately)

The setup wizard walks you through company creation, currency, fiscal year, and chart of accounts selection.


ERPNext Setup Wizard: First Configuration

Company and Chart of Accounts

Setup Wizard Step 1:
  Company Name: Acme Corp
  Abbreviation: ACME
  Default Currency: USD
  Country: United States
  Timezone: America/New_York

Step 2 — Chart of Accounts:
  Standard: US GAAP (or select your country's standard)
  → ERPNext imports ~200 standard accounts automatically

Fiscal Year

Accounting → Setup → Fiscal Year → New
  Year Name: 2026
  Year Start Date: 01-01-2026
  Year End Date: 12-31-2026

Core Modules Overview

Accounts (Financial Accounting)

ERPNext's Accounts module covers the full double-entry bookkeeping stack:

  • General Ledger: Chart of accounts, journal entries, ledger reports
  • Accounts Receivable: Customer invoices, payment tracking, aging reports
  • Accounts Payable: Supplier invoices, purchase orders, payment runs
  • Bank Reconciliation: Import bank statements, auto-match transactions
  • Tax Management: GST, VAT, US sales tax — configurable per jurisdiction
  • Multi-currency: Live exchange rates, realized/unrealized gain/loss tracking
  • Financial Statements: P&L, balance sheet, cash flow — generated in real time
Accounts → Reports → Profit and Loss Statement
  From Date: 01-01-2026
  To Date: 03-31-2026
  → Exports to PDF or Excel with one click

HR and Payroll

HR → Employee → New
  Full Name: Jane Smith
  Department: Engineering
  Designation: Software Engineer
  Date of Joining: 2026-01-15
  Salary Mode: Bank Transfer

Payroll → Salary Structure → New
  Structure: "Senior Engineer - US"
  Components:
    Earning:
      - Basic: formula = base * 0.40
      - HRA: formula = base * 0.20
      - Performance Bonus: amount = 5000
    Deduction:
      - Federal Tax: formula = (base + performance_bonus) * 0.22
      - 401k: formula = base * 0.05

Running payroll:

Payroll → Payroll Entry → New
  Company: Acme Corp
  Period: March 2026
  Payment Account: Payroll Bank Account
  → Process Payroll → Creates salary slips for all active employees

Inventory and Purchasing

Stock → Item → New
  Item Code: WIDGET-001
  Item Name: Acme Widget
  Item Group: Products
  Unit of Measure: Nos
  Valuation Method: FIFO

  Reorder Settings:
    Reorder Level: 100
    Reorder Qty: 500

ERPNext tracks inventory across multiple warehouses with real-time valuation. The stock ledger maintains a full audit trail of every movement.

Stock → Stock Entry → New
  Type: Material Receipt
  Items:
    - WIDGET-001: 500 units → Warehouse: Main → $2.50/unit
  → Submit → Updates inventory value and GL simultaneously

Manufacturing and MRP

Manufacturing → Bill of Materials → New
  Item: ASSEMBLED-WIDGET-001
  BOM Components:
    - WIDGET-BODY-01: 1 pc
    - WIDGET-SPRING-02: 2 pcs
    - WIDGET-FASTENER-03: 4 pcs
  Operations:
    - Assembly: 15 minutes @ $45/hr
    - QC Check: 5 minutes @ $35/hr

Material Requirements Planning (MRP) calculates what to buy and when:

Manufacturing → Production Planning → Run MRP
  Items: all items with pending Sales Orders
  → Creates Purchase Orders for raw materials
  → Creates Work Orders for production
  → Schedules by machine capacity and lead times

CRM and Sales

CRM → Lead → New
  Lead Name: John Doe
  Company: Prospect Corp
  Status: Open
  Lead Source: Website

CRM → Opportunity → Convert from Lead
  Opportunity Amount: $45,000
  Expected Close: 2026-04-30
  Items:
    - WIDGET-001: 1,000 units @ $45

The pipeline view shows all opportunities by stage, with close probability and expected revenue per rep.


Custom Domain and TLS

Step 1: Update DNS

# Add A record in your DNS provider:
erp.yourdomain.com → your.server.ip.address

Step 2: Add a Reverse Proxy with TLS

Add Caddy or Nginx Proxy Manager to your compose stack. With Caddy:

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
    depends_on:
      - frontend
# Caddyfile
erp.yourdomain.com {
  reverse_proxy frontend:8080
  encode gzip
}

Remove the ports: "80:8080" from the frontend service and add it to the same network as Caddy.

Step 3: Update ERPNext Site URL

docker exec -it erpnext-backend-1 bash
bench set-config -g hostname erp.yourdomain.com
bench setup nginx  # regenerates nginx config

Production Hardening

Automated Backups

#!/bin/bash
# backup-erpnext.sh
DATE=$(date +%Y%m%d_%H%M)
BACKUP_DIR="/backups/erpnext"

mkdir -p $BACKUP_DIR

# ERPNext built-in backup (includes DB + files)
docker exec erpnext-backend-1 \
  bench backup --with-files --site erp.yourdomain.com

# Copy backup to host
docker cp erpnext-backend-1:/home/frappe/frappe-bench/sites/erp.yourdomain.com/private/backups/. \
  $BACKUP_DIR/

# Upload to S3
rclone copy $BACKUP_DIR/ s3remote:backups/erpnext/

# Remove local backups older than 7 days
find $BACKUP_DIR -mtime +7 -delete

echo "ERPNext backup complete: $DATE"

Updates

cd ~/erpnext

# Pull new images
docker compose pull

# Stop, update, and restart
docker compose down
docker compose up -d

# Run migrations
docker exec erpnext-backend-1 bench migrate

Check the ERPNext releases page before major upgrades — v14→v15→v16 upgrades require running the migration command.

Performance Tuning

For large datasets (100k+ transactions), add these MariaDB settings:

  db:
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake
      - --skip-innodb-read-only-compressed
      - --innodb-buffer-pool-size=2G       # Set to 50-75% of available RAM
      - --max-connections=500
      - --query-cache-type=0               # Disable query cache (MariaDB 10.4+)

ERPNext vs Odoo: The Open Source ERP Comparison

Both ERPNext and Odoo are widely used open source ERPs. The key difference is in the license model:

ERPNextOdoo CommunityOdoo Enterprise
LicenseGPL v3 (all modules)LGPL (core only)Proprietary
Accounting✅ Full (free)LimitedFull (paid)
Manufacturing✅ Full (free)LimitedFull (paid)
Payroll✅ Full (free)✅ (paid)
eCommerce✅ Full (free)LimitedFull (paid)
Monthly cost (50 users)~$50 (VPS)Free + VPS$1,245/mo
UI/UXModern (v16)ModernModern
Implementation supportCommunity + partnersCommunity + partnersOdoo partners
Best forSMBs wanting full open sourceDev teams extending coreCompanies buying SaaS ERP

ERPNext's GPL v3 means every module is fully open source. Odoo Community is missing the modules most businesses actually need — Accounting, Manufacturing, and Payroll are behind the Enterprise paywall. This makes Odoo's "open source" framing misleading for most ERP use cases.


Frappe Cloud: Managed ERPNext Hosting

If you want ERPNext without the infrastructure management, Frappe (the company) offers managed cloud hosting:

frappe.cloud pricing (2026):
  Shared: $5/month — 1 site, up to 50 users, 5GB storage
  Dedicated: $25-100/month — dedicated VPS, more resources
  Enterprise: Custom pricing — dedicated infrastructure + SLA

Frappe Cloud runs the same open source ERPNext — no proprietary features, full data portability. Useful for teams that want the software without the ops overhead.


Methodology


Find more open source SAP and Odoo alternatives on OSSAlt — self-hosting guides, community ratings, and feature comparisons.

Related: Best Open Source Alternatives to Salesforce 2026 · Twenty vs EspoCRM 2026 · Self-Hosting vs Cloud: The Real Cost Comparison 2026

The SaaS-to-Self-Hosted Migration Guide (Free PDF)

Step-by-step: infrastructure setup, data migration, backups, and security for 15+ common SaaS replacements. Used by 300+ developers.

Join 300+ self-hosters. Unsubscribe in one click.