ERPNext is an open-source ERP built on the Frappe framework, covering accounting, sales, purchasing, inventory, manufacturing and projects. In this tutorial you will install ERPNext version 15 on Ubuntu 24.04 using Frappe Bench, the command-line tool that manages Frappe apps and sites. You will prepare MariaDB, Redis and Node.js, create a site, switch it to production mode with Nginx and Supervisor, enable HTTPS with Let's Encrypt and schedule backups.

Prerequisites

To follow this tutorial you need:

  • A fresh server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 4 GB of RAM (8 GB recommended for more than a few users) and 40 GB of free disk space.
  • A user with sudo privileges to prepare the system. You will create a dedicated frappe user in Step 1.
  • A domain or subdomain (this guide uses erp.your_domain) with a DNS A record pointing to your_server_ip. The site name will be this domain.

Step 1 - Creating the frappe user

Bench and all its files run under a dedicated non-root user. Create it and give it sudo, which bench needs for a few production setup commands:

sudo adduser frappe
sudo usermod -aG sudo frappe

On Ubuntu 24.04 new home directories are created with mode 750, which prevents Nginx (running as www-data) from serving the static assets that bench builds inside /home/frappe. Allow other users to traverse it:

sudo chmod 755 /home/frappe

Step 2 - Installing system dependencies

Install Git, Python build tools, pipx, Redis, MariaDB, Node.js, Nginx, Supervisor and the PDF renderer in one go:

sudo apt update
sudo apt install git curl cron python3-dev python3-venv python3-pip pipx \
  redis-server mariadb-server mariadb-client \
  nodejs npm nginx supervisor \
  xvfb libfontconfig1 wkhtmltopdf

Ubuntu 24.04 ships Python 3.12, Node.js 18 and MariaDB 10.11, which all meet the requirements of ERPNext 15. Frappe builds its frontend assets with Yarn, so install it globally:

sudo npm install -g yarn

Verify the versions:

python3 --version
node --version
yarn --version
mariadb --version
Python 3.12.3
v18.19.1
1.22.22
mariadb  Ver 15.1 Distrib 10.11.8-MariaDB, for debian-linux-gnu (x86_64) using  EditLine wrapper

Step 3 - Configuring MariaDB

Frappe requires the utf8mb4 character set. Create a configuration file for it:

sudo nano /etc/mysql/mariadb.conf.d/99-frappe.cnf
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

[mysql]
default-character-set = utf8mb4

Restart MariaDB to apply it:

sudo systemctl restart mariadb

When bench creates a site it connects to MariaDB as root with a password. On Ubuntu, root authenticates only through the Unix socket, so add a password while keeping socket login for sudo mariadb. Replace your_db_root_password with a strong password:

sudo mariadb -e "ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('your_db_root_password') OR unix_socket; FLUSH PRIVILEGES;"

Check both settings:

mariadb -u root -p -e "SHOW VARIABLES LIKE 'character_set_server';"
+----------------------+---------+
| Variable_name        | Value   |
+----------------------+---------+
| character_set_server | utf8mb4 |
+----------------------+---------+

Step 4 - Installing Frappe Bench

Switch to the frappe user. All remaining bench commands run as this user:

sudo -iu frappe

Ubuntu 24.04 blocks pip install into the system Python (PEP 668), so install bench with pipx, which puts it in its own virtual environment:

pipx install frappe-bench
pipx ensurepath

pipx ensurepath adds ~/.local/bin to your PATH. Log out and back in to apply it:

exit
sudo -iu frappe
bench --version
5.25.1

Your version number may differ.

Step 5 - Initializing the bench and getting ERPNext

A bench is a directory that holds the Frappe framework, the installed apps and the sites. Create one on the version 15 branch:

bench init --frappe-branch version-15 frappe-bench

This clones Frappe, creates a Python virtual environment, installs Node packages and builds assets, so it takes several minutes. When it ends you see:

SUCCESS: Bench frappe-bench initialized

Enter the bench and download ERPNext from the matching branch:

cd ~/frappe-bench
bench get-app --branch version-15 erpnext

Since version 14, HR and payroll live in a separate app called HRMS. If you need them, download it too:

bench get-app --branch version-15 hrms

Step 6 - Creating the site and installing ERPNext

Create a site named after your domain. Bench asks for the MariaDB root password from Step 3 and for a new password for the ERPNext Administrator user:

bench new-site erp.your_domain
MySQL root password:
Set Administrator password:
Re-enter Administrator password:
Installing frappe...
Updating DocTypes for frappe        : [========================================] 100%
*** Scheduler is disabled ***
Current Site set to erp.your_domain

Install ERPNext on the site (skip the hrms line if you did not download it), then enable the scheduler that runs background jobs such as emails and recurring documents:

bench --site erp.your_domain install-app erpnext
bench --site erp.your_domain install-app hrms
bench --site erp.your_domain enable-scheduler

Confirm the installed apps:

bench --site erp.your_domain list-apps
frappe 15.x.x version-15
erpnext 15.x.x version-15
hrms 15.x.x version-15

Step 7 - Setting up production with Nginx and Supervisor

In production, Supervisor keeps the Gunicorn web server, the Socket.IO server, the background workers and Redis running, and Nginx serves static files and proxies the rest. First, tell bench to match sites by domain name, so Nginx routes erp.your_domain to your site:

bench config dns_multitenant on

Bench is installed in the frappe user's ~/.local/bin, which is not in the PATH that sudo uses, so call it by its full path. Generate and enable the Nginx and Supervisor configuration:

sudo "$(command -v bench)" setup production frappe

Answer y if bench asks to overwrite existing configuration files. The command writes /etc/nginx/conf.d/frappe-bench.conf and /etc/supervisor/conf.d/frappe-bench.conf and reloads both services. Check the processes:

sudo supervisorctl status
frappe-bench-redis:frappe-bench-redis-cache                 RUNNING   pid 21873, uptime 0:00:40
frappe-bench-redis:frappe-bench-redis-queue                 RUNNING   pid 21874, uptime 0:00:40
frappe-bench-web:frappe-bench-frappe-web                    RUNNING   pid 21875, uptime 0:00:40
frappe-bench-web:frappe-bench-node-socketio                 RUNNING   pid 21876, uptime 0:00:40
frappe-bench-workers:frappe-bench-frappe-schedule           RUNNING   pid 21877, uptime 0:00:40
frappe-bench-workers:frappe-bench-frappe-short-worker-0     RUNNING   pid 21878, uptime 0:00:40
frappe-bench-workers:frappe-bench-frappe-long-worker-0      RUNNING   pid 21879, uptime 0:00:40

Open the web ports in UFW:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Check that Nginx serves the site:

curl -sI http://erp.your_domain | head -n 1
HTTP/1.1 200 OK

Step 8 - Enabling HTTPS

Bench regenerates /etc/nginx/conf.d/frappe-bench.conf whenever you run bench setup nginx or update, so rather than letting Certbot edit that file, store the certificate paths in the site configuration and let bench write the TLS block. Install Certbot and obtain a certificate using the Nginx plugin only for validation:

sudo apt install certbot python3-certbot-nginx
sudo certbot certonly --nginx -d erp.your_domain

Add the certificate paths to the site and regenerate the Nginx configuration:

cd ~/frappe-bench
bench --site erp.your_domain set-config ssl_certificate /etc/letsencrypt/live/erp.your_domain/fullchain.pem
bench --site erp.your_domain set-config ssl_certificate_key /etc/letsencrypt/live/erp.your_domain/privkey.pem
bench setup nginx
sudo nginx -t
sudo systemctl reload nginx

The generated configuration now listens on 443 and redirects HTTP to HTTPS. Nginx must be reloaded after each renewal to pick up the new certificate, so add a Certbot deploy hook:

sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
systemctl reload nginx
EOF
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo certbot renew --dry-run

Verify HTTPS:

curl -sI https://erp.your_domain | head -n 1
HTTP/2 200

Step 9 - Completing the setup wizard

Open https://erp.your_domain and log in as Administrator with the password from Step 6. The setup wizard asks for:

  1. Language.
  2. Region: country, time zone and currency.
  3. User: your own user account, which becomes a System Manager. Use it for daily work instead of Administrator.
  4. Organization: company name, abbreviation, the chart of accounts for your country and the fiscal year dates.

When the wizard finishes, ERPNext opens the home workspace with modules such as Accounting, Selling, Buying and Stock in the sidebar.

Step 10 - Scheduling backups

Bench can back up the database and uploaded files of a site. Run a backup manually first:

cd ~/frappe-bench
bench --site erp.your_domain backup --with-files
Backup Summary for erp.your_domain at 2026-09-25 10:42:13.512740
Config  : ./erp.your_domain/private/backups/20260925_104213-erp_your_domain-site_config_backup.json 245.0B
Database: ./erp.your_domain/private/backups/20260925_104213-erp_your_domain-database.sql.gz 1.2MiB
Public  : ./erp.your_domain/private/backups/20260925_104213-erp_your_domain-files.tar 10.0KiB
Private : ./erp.your_domain/private/backups/20260925_104213-erp_your_domain-private-files.tar 10.0KiB
Backup for Site erp.your_domain has been successfully completed with files

To run it every night at 02:00, edit the frappe user's crontab:

crontab -e
0 2 * * * cd /home/frappe/frappe-bench && /home/frappe/.local/bin/bench --site erp.your_domain backup --with-files > /dev/null 2>&1

Backups are written to ~/frappe-bench/sites/erp.your_domain/private/backups/. Watch the disk usage of that directory, and copy the files to storage outside the server, together with sites/erp.your_domain/site_config.json, which holds the database password and the encryption key.

Step 11 - Updating ERPNext

bench update pulls the latest commits of the version 15 branch for every app, runs database migrations, rebuilds assets and restarts the services. Take a backup first:

cd ~/frappe-bench
bench --site erp.your_domain backup --with-files
bench update

Moving to a new major version (for example 15 to 16) is a separate process: switch branches with bench switch-to-branch, following the official migration notes for that release.

Troubleshooting

  • Site loads without styles (unformatted HTML): Nginx cannot read the assets. Check that /home/frappe has mode 755 with ls -ld /home/frappe.
  • bench new-site fails with "Access denied for user 'root'@'localhost'": the MariaDB root password is not set. Repeat the ALTER USER command from Step 3.
  • 502 Bad Gateway: the web process is down. Run sudo supervisorctl status and read ~/frappe-bench/logs/web.error.log.
  • Emails or scheduled jobs never run: check that the scheduler is on with bench --site erp.your_domain scheduler status and that the worker processes are RUNNING in Supervisor.

Conclusion

ERPNext 15 is now running on Ubuntu 24.04 in production mode, with Nginx and Supervisor managing the services, HTTPS from Let's Encrypt and nightly backups. As next steps, configure an outgoing email account under Email Account, create users and assign roles for your team, and import your items, customers and opening balances with the Data Import tool.