How to Install ERPNext v16 on Ubuntu 22.04

Below are the steps I used to install this website using ERPNext version 16 hosted on a VPS running Ubuntu 22.04.

 · 9 min read

#!/bin/sh

# Install ERPNext v16 Sites on Ubuntu 22.04


# This code is not fully executable, but it is a starting point for a fully exicutable install script.  If modified to be run as a script, first give permissions to run, then execute:

# chmod +x install_erpnext_v16_server.sh

# ./[THIS_FILE_NAME]


# Thanks to:

#     • https://medium.com/@ibrahim.ah888/step-by-step-guide-to-installing-erpnext-16-beta-on-ubuntu-22-04-lts-484e62f0d480

#     • https://medium.com/@ankitjangir.1690/how-i-deployed-frappe-in-production-on-ubuntu-24-04-aws-ec2-2945589d329d


# The goals are:

# • Install Frappe Bench v16

# • Install ERPNext v16

# • Setup SSL certs and auto-updates (with certbot or getssl)

# • Setup proper firewalls

# • Ensure sites run in production mode (with redis and supervisor)

# • Automate backups


# Next steps include:

# • Login to create first user and initial business information

# • Build public-facing homepage

# • Integrate Google services - email account, calendar, indexing, and analytics

# • Update Contact Us page

# • Setup ecommerce shop










####################################################################################


# Ensure DNS of hostname points to the server (www and non-www)


# Reboot Virtual Private Server (VPS)

# 1. Log into hosting.com and find VPS under Products & Services > Hosting & Servers.

# 2. Click Login next to the server name. In my case this is a 4GB Memory Runway 4 VPS.

# 3. Click Settings/Manage icon.

# 4. Click the Install tab.

# 5. Select Ubuntu 22.04 and enter the desired root password for the VPS. Then click Reinstall.


# Log into server via SSH:

# 1. Note IP address of server, visible while logged in to manage it on hosting.com.

# 2. Open Linux terminal and run this command to connect:

# ssh -p 7822 root@[IP_Address]

# (Enter chosen root password from server reboot when prompted.)




####################################################################################

# As root user...

####################################################################################


# Update system and install dependencies

sudo apt-get update -y

sudo apt-get upgrade -y

sudo apt-get install -y git wget curl build-essential software-properties-common


# Create [USER] that will serve as SysAdmin.


# Create user

sudo adduser [USER] --gecos "[USERS_NAME],,,," --disabled-password


# Set a strong password

sudo passwd [USER]


# Add [USER] to sudo group

sudo usermod -aG sudo [USER]


# Switch to [USER]

sudo su - [USER]



####################################################################################

# Run everything below as (sysadmin) [USER], not root, unless otherwise specified.

####################################################################################




####################################################################################

# Install python and other dependencies

####################################################################################


# Install uv

curl -LsSf https://astral.sh/uv/install.sh | sh


source $HOME/.local/bin/env


# Ensure programs installed on this path can be used as sudo

sudo visudo

# Append :/home/[USER]/.local/bin to the end of secure path variable


# Install python 3.14 and set as default

uv python install 3.14 --default


# Install development libraries

sudo apt-get install -y \

python3-dev \

python3-pip \

python3-setuptools \

python3-distutils \

python3-venv \

libssl-dev \

libffi-dev \

libxml2-dev \

libxslt1-dev \

zlib1g-dev \

libsasl2-dev \

libldap2-dev \

libjpeg-dev \

libcups2-dev \

libpq-dev \

libtiff5-dev \

libmysqlclient-dev


# Verify Python version

python3 --version # Should show Python 3.14.x


# Install wkhtmltopdf for PDF generation - this may not be needed with the newest frappe/erpnext updates.

sudo apt-get install -y xvfb libfontconfig wkhtmltopdf









####################################################################################

# Install and configure nginx

####################################################################################


# Install nginx and check version

sudo apt-get install -y nginx

nginx -v # 1.18.0


# Stop and disable Apache2

sudo systemctl stop apache2

sudo systemctl disable apache2


# Start and enable nginx

sudo systemctl start nginx

sudo systemctl enable nginx

sudo systemctl status nginx


# Avoid main log issue when running bench setup nginx...

sudo vim /etc/nginx/nginx.conf


####################################################################################

# # Uncomment and insert the below above the log file names, save and exit.

#

# log_format main '$remote_addr - $remote_user [$time_local] "$request" '

# '$status $body_bytes_sent "$http_referer" '

# '"$http_user_agent" "$http_x_forwarded_for"';

#

####################################################################################





####################################################################################

# Install supervisor and ansible

####################################################################################


# Not sure why I hit an error with supervisor when only installing via uv

# Hence, listing steps to install via uv, plus apt, plus enable and start.

sudo uv pip install --system supervisor


# sudo apt update

# sudo apt install supervisor

sudo systemctl enable supervisord

sudo systemctl start supervisord


# Needed to run frappe bench in production mode

uv tool install --with-executables-from ansible-core ansible


####################################################################################

# Install and configure MariaDB

####################################################################################


# Add MariaDB repository

curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=11.8

# NOTE: previously used 10.6


# Install MariaDB

sudo apt-get update

sudo apt-get install -y mariadb-server mariadb-client


# Start and enable the service

sudo systemctl start mariadb

sudo systemctl enable mariadb


# Verify installation

mysql --version

sudo mysql_secure_installation

# Answer Y to all prompts, except I used passwords, not sockets.



sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf


###############################################################################

# Uncomment and insert only the following three lines under [mysqld]


# character-set-client-handshake = FALSE

# character-set-server = utf8mb4

# collation-server = utf8mb4_unicode_ci


###############################################################################


sudo systemctl restart mariadb


sudo mysql -u root -p -e "CREATE USER '[USER]'@'localhost' IDENTIFIED BY '[PASSWORD]';"

sudo mysql -u root -p -e "GRANT ALL PRIVILEGES ON *.* TO '[USER]'@'localhost' WITH GRANT OPTION;"

sudo mysql -u root -p -e "FLUSH PRIVILEGES;"


# Restart mariaDB

sudo systemctl restart mariadb

sudo systemctl status mariadb # Should be active



###############################################################################

###############################################################################

# MariaDB Troubleshooting notes:


# Main configuration file (for reference)

# sudo vim /etc/mysql/my.cnf


# Uncomment and insert the below...

# Actually not sure performance settings are needed.


# WHICH OF THESE IS OPTIMAL? WHERE?

# Options pulled from blog posts mentioned at top of file.


# [mysqld]

# character-set-client-handshake = FALSE

# character-set-server = utf8mb4

# collation-server = utf8mb4_unicode_ci


# # Performance Settings

# innodb_file_format = Barracuda

# innodb_file_per_table = 1

# innodb_large_prefix = 1

# innodb_buffer_pool_size = 1G

# max_connections = 200

# max_allowed_packet = 256M[mysql]


# [mysql]

# default-character-set = utf8mb4


###############################################################################

# The command and config below led to an error when initializing the site, but this is a good command to be able to fully automate this script in the future.  I ended up writing the configs to the maria conf.d file, not the main conf file.

# Tee is like a tee pipe in plumbing, splitting the flow here to both the config file and /dev/null, which is like a black hole where whatever's written to it vanishes rather than going to stdout. EOT (Originally End of Transmission) is a deliniator to mark start and end of multiline input.


# sudo tee -a /etc/mysql/my.cnf > /dev/null << EOT

# [mysqld]

# character-set-client-handshake = FALSE

# character-set-server = utf8mb4

# collation-server = utf8mb4_unicode_ci

# [mysql]

# default-character-set = utf8mb4

# EOT

###############################################################################


# Potentially identify user by socket instead of password:

# sudo mysql -u root -p -e "CREATE USER '[USER]'@'localhost' IDENTIFIED VIA unix_socket;"

# sudo mysql -u root -p -e "GRANT ALL PRIVILEGES ON *.* TO '[USER]'@'localhost' WITH GRANT OPTION;"

# sudo mysql -u root -p -e "FLUSH PRIVILEGES;"


###############################################################################

###############################################################################








####################################################################################

# Install Redis

####################################################################################


# Install Redis

sudo apt-get install -y redis-server


# Start and enable

sudo systemctl start redis-server

sudo systemctl enable redis-server


# Test it's working

redis-cli ping # Should return: PONG







####################################################################################

# Install Node.js and Yarn

####################################################################################


# Install NVM

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash


# Load NVM into your session

export NVM_DIR="$HOME/.nvm"

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"


# Install Node.js

nvm install --lts

nvm use --lts

# nvm alias default lts/*


# Verify

node --version # Should show v24.x.x

npm --version # Should be 11.x.x


# Install Yarn

npm install -g yarn

yarn --version







####################################################################################

# Install Frappe Bench

####################################################################################


# Install Bench CLI

uv tool install frappe-bench --python python3.14


# Verify installation

bench --version





####################################################################################

# Initialize Bench Directory

####################################################################################


# Here the bench folder is named bench-v16, but other names can also be used.


# Navigate to home directory if not already there

cd /home/[USER]


# Initialize bench

bench init --frappe-branch version-16 --verbose bench-v16


# Set proper permissions

chmod -R o+rx /home/[USER]








####################################################################################

####################################################################################

# Initialize Websites:

####################################################################################

####################################################################################







####################################################################################

# Create site

####################################################################################


# Navigate to bench directory

cd ~/bench-v16


# Create a new site

bench new-site [SITE-NAME]


# Enter [USER] you granted privileges when prompted for superuser.

# Enter MariaDB password when prompted.

# Create new Administrator password when prompted.








####################################################################################

# Get apps and install them to website…

####################################################################################


# Suggested App Installation...


# Download apps

bench get-app --branch version-16 erpnext

bench get-app --branch version-16 blog


# # Maybe not worth using yet on v16:

# bench get-app --branch version-16 hrms  # May be worth using if employees are needed.

# bench get-app --branch version-15 payments

# bench get-app --branch version-15 webshop

# bench get-app --branch develop agriculture

# bench get-app --branch develop lms  # Errors when running bench build.

# bench get-app --branch main crm

# bench get-app --branch main insights


# Install apps to site

bench --site [SITE] install-app [APP(s)]


# Verify installation to site

bench --site [SITE] list-apps


# Migrate to update DB

# This step only needs to be done for new apps on existing initialized sites

# bench --site [SITE] migrate



####################################################################################

# Build other site(s)

####################################################################################


# If hosting multiple sites, create the others now...


# Maybe create new site named [IP_ADDRESS] and set as default

bench new-site --set-default [IP_ADDRESS]


# Install apps as desired.


# Error may be thrown that common config file is not available if only one site has been created when configuring multitenant.




####################################################################################

# Setup DNS Multitenancy to generate cert for www traffic

####################################################################################


cd ~/bench-v16

bench config dns_multitenant on

# Add alt domain for each site

bench setup add-domain --site [SITE] www.[SITE]


# Reload

bench setup nginx

sudo service nginx reload





####################################################################################

# Test sites with dev server

####################################################################################


cd ~/bench-v16

bench start

# Open new incognito window and see if site is available at [IP_ADDRESS]:[PORT]

# Terminal will mention which port is being used

# Ctrl+C to stop the server.





####################################################################################

# Setup and launch production

####################################################################################


# Add PATH as environment variable to supervisor config file

# If not done, an error occurs in some functions on the website, such as creating a new theme.

echo $PATH  # This will show your path.  Copy it to insert as shown below.

vim /home/[USER]/bench-v16/config/supervisor.conf


# Under [program:bench-v16-frappe-web] insert the following (uncommented):

# environment=PATH="[YOUR_PATH]"



# Launch production server

sudo bench setup production [USER]


####################################################################################

# ERROR HANDLING STEPS


# If sudo bench error is thrown, update path variable...


# sudo visudo

# # Appended :/home/[USER]/.local/bin to end of secure path variable


# If Ansible error is thrown…


# # install ansible via uv

# uv tool install --with-executables-from ansible-core ansible



# If fail2ban error is thrown…


# sudo apt-get install fail2ban -y

# sudo systemctl enable fail2ban

# sudo service fail2ban start

# sudo fail2ban-client status


####################################################################################


# Navigate to site(s) in a new incognito window. They should be available.






####################################################################################

# Setup SSL with getssl and automate updates

####################################################################################


# Using certbot, which may be easier…

sudo apt-get install -y certbot python3-certbot-nginx


sudo bench setup lets-encrypt [SITE]

# # Follow the prompts:

# # - Enter email for renewal notifications

# # - Agree to Terms of Service

# What this does:

# • Obtains free SSL certificate from Let’s Encrypt

# • Configures Nginx automatically

# • Sets up auto-renewal

# • Redirects HTTP to HTTPS



# Certificate is saved at: /etc/letsencrypt/live/[SITE]/fullchain.pem

# Key is saved at: /etc/letsencrypt/live/[SITE]/privkey.pem






####################################################################################

# Alternatively, setup SSL with getssl and automate updates

####################################################################################


# If certbot and lets-encrypt steps did not work, follow below steps using getssl and manually configuring nginx.


# May need to first navigate to site in browser, log in as admin, and create first user and complete setup, but I don’t think so.


# Get script for getssl

sudo curl --silent https://raw.githubusercontent.com/srvrco/getssl/latest/getssl > getssl ; chmod 700 getssl


# Create config file for each site

./getssl -c [SITE-NAME]


# Edit config file for each site

vim ~/.getssl/[SITE-NAME]/getssl.cfg


# Uncomment the server that issues full certs to use it.

# Leave SANS=”www.[SITE]” to allow www access (which will be later redirected via nginx config

# Set USE_SINGLE_ACL="true"

# Set ACL=(‘/home/[USER]/bench-v16/sites/[SITE]/public/.well-known/acme-challenge’)


# Set the locations for crt, key, and crt chain:

# DOMAIN_CERT_LOCATION="/etc/ssl/[SITE].crt" # this is domain cert

# DOMAIN_KEY_LOCATION="/etc/ssl/[SITE].key" # this is domain key

# DOMAIN_CHAIN_LOCATION="/etc/ssl/[SITE].chain.crt" # this is the domain cert and CA cert


# Save and exit


# Choice of CA server in individual site config takes precedence, so no need to edit main config file, but for reference...

# vim ~/.getssl/getssl.cfg


# Generate SSL certs for each site

sudo ./getssl [SITE-NAME]


# Navigate into bench directory

cd ~/bench-v16


# Set SSL cert and key for each site

bench set-ssl-certificate [SITE-NAME] /etc/ssl/[SITE-NAME].crt

bench set-ssl-key [SITE-NAME] /etc/ssl/[SITE-NAME].key


bench setup nginx

sudo nginx -t

sudo systemctl reload nginx

sudo systemctl status nginx



####################################################################################

# Automate SSL cert updates via getssl via cron jobs

####################################################################################


# List cron jobs

crontab -l


# Edit cron jobs

sudo crontab -e

# Alternative to edit cron jobs

sudo vim /etc/crontab



# With either vim or crontab command, create the following cron jobs.

# Sudo must be used or there will be a permission error.


# Add the following two cron jobs to automate ssl cert updates…


# Update certs at 1:01 on the first week of every month.

1 1 1 * * /home/[USER]/getssl -u -a -q


# Reload nginx at 1:05 first week of every month

5 1 1 * * systemctl reload nginx





####################################################################################

# Configure nginx to redirect www traffic and only use https

####################################################################################


# Create nginx config file to redirect www traffic to plain http://

sudo vim /etc/nginx/conf.d/redirect.conf


####################################################################################

# Uncomment and insert the server block shown below for each site, save and exit.

# Highlight and ctrl+/ to comment/uncomment in vscodium.

# Double-check, but the last block should be set up by the bench config, so likely just add the first two blocks...


# server {

#     server_name www.example.com example.com;

#     return 301 https://example.com$request_uri;

# }


# server {

# listen 443 ssl;

# ssl_certificate /path/to/server.cert;

# ssl_certificate_key /path/to/server.key;

#     server_name www.example.com;

#     return 301 https://example.com$request_uri;

# }


# server {

# listen 443 ssl;

# ssl_certificate /path/to/server.cert;

# ssl_certificate_key /path/to/server.key;

# server_name example.com;


# <locations for processing requests>

# }

####################################################################################


####################################################################################

# Possible alternative...

# server {

# listen 80;

# server_name ~^www\.(?<domain>.+)$;

# return 301 https://$domain$request_uri;

# }

####################################################################################


# Test configs

sudo nginx -t


# Reload nginx

sudo systemctl reload nginx









####################################################################################

# Configure firewall

####################################################################################


# Install firewall

sudo apt-get install -y ufw


# Allow necessary ports

sudo ufw allow 22/tcp # SSH

sudo ufw allow 7822/tcp # SSH

sudo ufw allow 80/tcp # HTTP

sudo ufw allow 443/tcp # HTTPS


# Enable firewall

sudo ufw --force enable

sudo ufw status





####################################################################################

# Automate backups (if not already done.)

####################################################################################


# # Create backup directory

# mkdir -p ~/backups


# # Edit crontab

# crontab -e


# # Add daily backup at 2 AM

# 0 2 * * * cd /home/[USER]/bench-v16 && /home/[USER]/.local/bin/bench --site [SITE] backup --backup-path /home/[USER]/backups >> /home/[USER]/backup.log 2>&1








####################################################################################

# Next Steps:

####################################################################################


# Install sites and setup key configurations:

# 1. Login with Administrator user and password set during site creation.

# 2. Create key Users

# 3. Configure email


# After initializing site via web UI, run:

bench build --production


# If yarn error is thrown, run

bench setup requirements




No comments yet.

Add a comment
Ctrl+Enter to add comment