Why self-host ERPNext on a VPS
An ERP centralises the company's most sensitive data: accounting entries, margins, customer and supplier records, payroll. SaaS offers often bill per user per month, which becomes costly as teams grow. On a dedicated VPS, you pay for the server once, you add as many users as needed, and the database stays under your control, backed up according to your own rules.
ERPNext is built on Frappe, a Python/JavaScript framework that keeps the source code readable and extensible. A self-hosted instance lets you update on your own schedule, choose add-on apps (HRMS, e-Commerce, Education, Healthcare) and write your own modules without relying on a vendor.
The concrete benefits of a self-hosted ERPNext
- No per-user cost: add your teams without increasing the bill.
- Sovereignty over accounting and HR data, which stays on your server.
- All modules available (accounting, inventory, CRM, manufacturing, payroll) with no paid tier.
- Free customisation through the Frappe framework and complementary apps.
- Controlled backups and retention, tailored to your legal obligations.
- Scalability: increase RAM/vCPU as the transaction volume grows.
ERPNext's key modules
ERPNext covers the full operational cycle of a business. Accounting: multi-currency chart of accounts, general ledger, statutory reports (balance sheet, profit & loss), VAT management and bank reconciliation. Sales and purchasing: quotes, orders, deliveries, invoices and credit notes with approval workflows. Inventory management: multiple warehouses, batches and serial numbers, rolling stock counts, FIFO or moving-average valuation. CRM: leads, opportunities, campaigns and sales pipeline with an activity dashboard. Manufacturing: bill of materials (BOM), work orders, workstation and scrap tracking. HR: employee records, leave, attendance, country-configurable payroll and performance appraisals.
Each module is activated from the ERPNext desk; you only install what you need, and access rights are defined by role at the document level.
Hardware and software requirements
ERPNext is more demanding than average. Plan for at least 2 vCPU and 4 GB of RAM for test use, and 4 vCPU / 8 GB of RAM for production with several concurrent users. Allow 20 GB of disk for the MariaDB database, Redis and uploaded files.
On the software side: Docker and Docker Compose v2 (>= 2.20), a domain name (e.g. erp.myapp.com) pointing to the VPS IP, and port 443 open inbound. ERPNext uses MariaDB 10.6+ as its primary database and Redis for caching and real-time workers — both are included in the frappe_docker stack; you do not need to install them separately.
Deploy ERPNext with Frappe Docker and HTTPS
Prepare the VPS and Docker
Over SSH, update the system (
apt update && apt upgrade -y) and install Docker viacurl -fsSL https://get.docker.com | sh. Add your user to the docker group:usermod -aG docker $USER. Verify withdocker compose version(v2 required).Get frappe_docker
Clone the official repository and pick the branch matching the target version:
git clone https://github.com/frappe/frappe_docker cd frappe_dockerThe
overrides/folder contains compose partials for Caddy (auto-HTTPS), Traefik and the HRMS app — assemble them according to your needs.Configure the environment
Copy
example.envto.envand adjust the key variables:cp example.env .envIn
.env, setFRAPPE_SITE_NAME_HEADER,DB_PASSWORD,REDIS_CACHE, and — if you use the Caddy override —LETSENCRYPT_EMAILand the domain inCaddyfile. Never deploy with default passwords.Launch the stack
Start all services:
docker compose --project-name erpnext \ -f compose.yaml \ -f overrides/compose.mariadb.yaml \ -f overrides/compose.redis.yaml \ -f overrides/compose.https.yaml \ up -dThen create the ERPNext site and install the app:
docker compose exec backend bench new-site erp.myapp.com \ --mariadb-root-password <rootpwd> \ --admin-password <adminpwd> docker compose exec backend bench --site erp.myapp.com install-app erpnext docker compose exec backend bench --site erp.myapp.com migrateFollow the logs to confirm all services (db, redis, workers, scheduler) are healthy:
docker compose logs -f.Configure the reverse proxy
If you are not using the included Caddy override, place Nginx in front of ERPNext. Minimal example:
server { listen 443 ssl; server_name erp.myapp.com; ssl_certificate /etc/letsencrypt/live/erp.myapp.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/erp.myapp.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; } }With Caddy, a simple
Caddyfileis enough:erp.myapp.com { reverse_proxy backend:8000 }— the Let's Encrypt certificate is obtained and renewed automatically.Secure and back up
Restrict internal ports (MariaDB 3306, Redis 6379) to loopback or the internal Docker network. Schedule regular backups with
bench backupand store them off the VPS:docker compose exec backend bench --site erp.myapp.com backup --with-filesBackup files are placed in
sites/erp.myapp.com/private/backups/inside thesitesvolume — copy them to external object storage (S3, Garage) for disaster recovery.Logging in for the first time
Open your application's address and sign in with the username
Administratorand the admin password set duringbench new-site. Allow a few minutes after installation before the address responds — the initial migrations run in the background. ERPNext then moves on to its setup wizard: language, time zone, currency, fiscal year and company.
Post-installation: company, fiscal year and users
Once logged in as Administrator, go to Settings → System Settings to confirm the time zone and date format, then to Accounting → Chart of Accounts to adapt the structure to local standards (ERPNext provides charts of accounts for around a hundred countries).
Create your company under Accounting → Company: enter the base currency, tax number and opening fiscal year. ERPNext automatically creates the closing accounts and opening balance entries.
For users, go to Settings → Users and Permissions → User: assign business roles (Accounts Manager, Stock User, HR Manager…) rather than document-by-document rights. Roles combine: a user can be both Sales User and Purchase User without admin rights.
Monitoring and maintenance
ERPNext provides several entry points for supervising a production instance.
bench doctor is the built-in diagnostic command: it checks the status of Celery workers, the scheduler and the Redis connection. Run it from the container:
docker compose exec backend bench doctorA missing worker or blocked queue shows up immediately.
Application logs — ERPNext logs are in sites/<site>/logs/ inside the sites volume:
- web.log: HTTP errors and Python traces from the Gunicorn backend;
- worker.error.log: Celery worker exceptions (scheduled tasks, email sending);
- scheduler.log: scheduler cycles.
Automated backup — set up a cron job on the host to run bench backup daily and copy archives to remote storage:
0 3 * * * docker compose -p erpnext exec -T backend \
bench --site erp.myapp.com backup --with-files \
&& rclone copy /path/to/backups remote:erpnext-backupsUpdates — before each major version upgrade, do a full backup, read the release notes, test on a copy, then: bench update --reset inside the container. Schema migrations apply automatically, but some major ERPNext versions require upgrading to the highest minor version first (e.g. v14 → v14.x latest before moving to v15).
Troubleshooting: common errors
Worker crash: RedisBroadcastError or ConnectionRefusedError to Redis
Error in worker.error.log: redis.exceptions.ConnectionRefusedError: [Errno 111] Connection refused. Cause: the Redis container stopped or restarted after an OOM. Check: docker compose ps redis — if it is in Exited, restart with docker compose up -d redis then restart workers. If the crash recurs, increase the VPS RAM or limit the worker count in common_site_config.json.
Schema migration stuck: frappe.exceptions.SchemaChangedError
Error during bench migrate: SchemaChangedError: <DocType> has been manually modified. Cause: a column was manually changed in the database, ERPNext refuses to overwrite. Fix: bench --site erp.myapp.com migrate --skip-failing, then inspect the affected doctype in the UI and rerun migrate without the flag.
Bench timeout: Traceback ... requests.exceptions.ReadTimeout
Error during a long action (mass CSV import, inventory recalculation): ReadTimeout: HTTPConnectionPool. Increase the Gunicorn timeout in common_site_config.json: "gunicorn_workers": 2, "web_timeout": 120. Restart the web service: docker compose restart backend.
MariaDB connection refused: OperationalError: (2003, "Can't connect to MySQL server on 'db'")
Error at bench new-site or worker startup. Usual cause: the MariaDB container is not ready yet or the health check failed. Check: docker compose logs db | tail -20. If MariaDB shows [ERROR] InnoDB: Page 0 log sequence number, the database files are corrupted — restore from the last backup. If it is just a startup delay, wait 30 seconds and rerun the command.
Blank site after bench migrate: TemplateNotFound
ERPNext shows a blank page or Jinja error after an update. Cause: static assets were not rebuilt. Run: docker compose exec backend bench --site erp.myapp.com clear-cache && bench build --app erpnext. Restarting the frontend container (if separate) may also suffice.
ERPNext vs Odoo Community: when to choose one or the other
Scroll the table
| Criterion | ERPNext | Odoo Community |
|---|---|---|
| Licence | GPL v3 — code and modules free | LGPL v3 (core) — Enterprise modules proprietary |
| Included modules | Accounting, inventory, CRM, manufacturing, HR — all free | Functional core; advanced modules reserved for paid Odoo Enterprise |
| Language / stack | Python + Frappe, vanilla JS on the client side | Python + OWL (in-house JS framework) |
| Interface | App desk, configurable forms without code | Kanban/list view, low-code studio (Enterprise) |
| Installation complexity | Well-documented official Docker stack | Docker available, but less community track record |
| Community | Active on GitHub and Frappe forum; strong in India, Africa | Very large; dense partner ecosystem in Europe |
| Ideal for | SMBs seeking a fully free ERP, manufacturing, multi-currency | Companies wanting advanced CRM or Odoo e-commerce without custom development |
ERPNext evolves through major versions with schema migrations: before each update, make a full bench backup and test the version upgrade on a copy before applying it in production. Migrations between major versions must follow the official upgrade path (e.g. v14 → v15 without skipping a version).
The official documentation
For advanced configuration and tool-specific options, refer to the official ERPNext documentation and the frappe_docker repository. This guide covers going live on a VPS; the vendor documentation remains the reference for fine-tuning, major version upgrades and specific use cases.