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.
#!/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:
# 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. Login to start a new discussion Start a new discussion