Installing ERPNext on Kubernetes has advantages in scalability, stability, and elasticity. However, installing on a VM can also be beneficial:
- easier debugging with “ad-hoc” techniques
potential use of AWS Arm Graviton2 instances(too many issues, Frappe will not support it)- remote debugging using Code Server or Gitpod
Launch Lightsail or EC2 Instance
You can use EC2 (t3a.medium) or Lightsail with 4 GB (bundle medium_2_0
). Using Pulumi is recommended. With 3.7 GiB, ERPNext uses 44% after running, but it could have used more during initial installation. A running production ERPNext with nginx, mariadb uninstalled, system redis-server disabled (only uses supervisor-managed redis), uses 623Mi, so it’s possible you can run it with t3a.micro.
Note about arm64 / Graviton2: Hendy tried to use Arm (m6g.medium 4GiB or t4g) but with several issues. Notably, v13.10.0 breaks due to pypika.
For EBS, 8 GiB is too small and won’t even complete initial bench installation, set at least 12 GiB. After installation, / partition usage is at 8.7 GiB.
For security groups, select “default” and “ssh-server”. No need to expose HTTP/HTTPS ports because we’ll use an ALB.

Prepare Ubuntu System Configuration
Set hostname:
Enable byobu and make sure LC_ALL
is set by server even when connecting remotely:
Then re-login using ssh.
Update Ubuntu packages:
To enable VSCode to watch files, please apply the following system tweak to the server.
Troubleshooting: UnhandledPromiseRejectionWarning: Error: ENOSPC: System limit for number of file watchers reached
Solution:
Install ERPNext version-13 and Create a Site Manually using Bench in Production Mode
References:
- Frappe Bench README – Manual Installation
- Frappe Bench – Installation
- The Hitchhiker’s Guide to Installing Frappe on Linux
Note: This will install MariaDB locally, which is OK. Later you can dump & restore the data from local MariaDB to AWS MariaDB, then purge the local MariaDB installation.
- [If you install with Pulumi and/or use Lightsail User Data to do initial update] Wait until apt-get -y full-upgrade completes:
sudo ps -ef | grep apt
Make sure there is no apt* running. - Reboot the server: sudo reboot
- Install Prerequisites.
- Install MariaDB 10.5 locally. Make sure to store the root password in a safe place.
Check status
Install libmysqlclient-dev
Edit /etc/mysql/my.cnf:
And append this to the end of file
Install Redis:
Install NodeJS 14:
Install yarn Classic:
Install wkhtmltopdf (check latest version in their website):
Create frappe user:
Add the lovia-production public key into ~/.ssh/authorized_keys.
Install frappe-bench: (as root, because will be needed by “sudo bench setup production”)
Log out (Ctrl+D) then log back in as frappe.
Check bench version:
Initialize bench:
Enable site based multi-tenancy:
Get ERPNext app:
Get custom apps:
Create site:
Setup Production (supervisor and nginx, Cloudflare SSL)
To use Cloudflare SSL, you need to enable nginx SSL with snake-oil.
Check HTTP/2 support:
Moving an ERPNext Site to Another Server (Backup & Restore Database and Files)
Reference:
- https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/data/download-backup
- https://github.com/frappe/erpnext/wiki/Restoring-From-ERPNext-Backup
- https://discuss.erpnext.com/t/transfer-erpnext-from-one-server-to-another/8657
Backup first: https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/data/download-backup
You will get a report with 4 files: (note: If you are confused with the file names, it’s because it uses the site’s time zone, not the server time zone)
IMPORTANT: You will also need to backup site_config.json
which has encryption key! Because if you lost it:
- For each user that have API key, regenerate the API Key
- Go to Email Account List and re-input all IMAP/SMTP passwords
- ERPNext Mobile App log in error with Error 417. -> Use Frappe mobile instead (source).
Later, you will need to transfer these 4 files to the new server. But first, in the target server, you’ll need to create a new site.
Edit site_config.json and edit the encrypted secret to match the old site.
Now you’ll need to transfer the actual backup files to the new server. In new server, create a SSH keypair using ssh-keygen -o -a 100 -t ed25519
. Then add the public key to old server’s ~/.ssh/authorized_keys.
Then you can copy the files:
Then do the restore: (prepare your MariaDB root password)
After restore is done and successful, you can remove the public key from old server’s ~/.ssh/authorized_keys.
Development Configuration
Problem with AWS NLB/ALB health check is you can’t set the Host header, it’s always private IP (e.g. 172.30.1.228): (ALB documentation)
Solution for development: A better way is to mimic production configuration’s by installing nginx and using bench setup nginx
(see below). Now you can use bench start --no-dev
, and frappe.socketio client will use the standard HTTPS port instead of 9000.
Workaround for development setup:
Install nginx-light: sudo apt install nginx-lightSet the health check to port 80

Production Configuration
Reference: https://frappeframework.com/docs/user/en/bench/guides/setup-production#nginx
Production configuration uses a combination of supervisor and nginx (nginx reverse-proxies the /socket.io endpoint as well):
Enable DNS/Host based multitenancy.
With Host-based multitenancy, you need to “restore” the sites-enabled nginx configuration style that was replaced by Bench Easy Install script. Add to appropriate block in /etc/nginx/nginx.conf
:
Then enable the default site:
Without the above, you’ll fail ALB health checks because all requests will be forwarded to frappe-web, in addition to spamming logs/frappe.web.logs with 404 errors.
To regenerate nginx configuration in ~/frappe-bench/config/nginx.conf
(which is symlinked by /etc/nginx/conf.d/frappe-bench.conf
):

Troubleshooting: BunnyCDN does not support WebSockets
As of October 2020, BunnyCDN does not support WebSockets. That makes it unusable for ERPNext in default configuration.
We can use AWS CloudFront or Cloudflare instead.
Troubleshooting 1: LC_ALL
The “Run the following commands in shell: export LC_ALL=C.UTF-8” warning is caused by ssh client sending the (conflicting) LC_ALL environment variable to remote server. What you can do:
Then re-login SSH.
For the launchpadlib error, you can try reinstalling manually by:
That is actually a bug since Easy Install should have stopped on that error, instead of continuing.
Troubleshooting 4: SocketIO Connects to Port 9000
Problem: When using develop environment, Desk UI tries to connect to https://erp-staging.lovia.life:9000/socket.io/?EIO=3&transport=polling&t=NJoyMs0 instead of regular HTTPS port.
You can check config by: bench –site erp-staging.lovia.life show-config
In proper working Kubernetes production environment, common_site_config.json
is:
This is in JavaScript client code, desk.js -> socketio_client.js -> frappe.socketio.init() -> frappe.socketio.get_host()
Both dev and prod (Kubernetes) on this socketio_client.js code, input port defaults to 3000.
In implementation of “frappe.socketio.get_host(port)” changes the actual host, due to window.dev_server==true and frappe.boot.socketio_port==9000:
Although you can run bench start –no-dev but this will result in Error 404 as the bench server does not reverse proxy to socketio server. So the least annoying workaround is:
1. open port 9000, and
2. Configure load balancer for that too. Don’t forget to set ALB’s security group including “socketio-9000-server”.
3. Meaning you can’t use CDN for ERPNext development/staging environment.
If it goes well you’ll get 101 Switching Protocols response on your browser:

Migrating Sites using Bench Backup & Restore
Reference: https://discuss.erpnext.com/t/backup-restore/5675
Two ways:
- Bench Backup & Restore
- Manually rsync the sites/SITE_NAME and MariaDB backup. If the source is using Kubernetes, you can do the rsync from the Kubernetes runnning container. You may need to “apt install openssh-client rsync” first.
Then. For development:
Or for production:
Code Server
- Add frappe to
using visudo
: - frappe ALL=(ALL) NOPASSWD:ALL
- Switch to frappe
- Install code-server
- Edit ~/.config/code-server/config.yamll, change bind-addr to 0.0.0.0
- sudo systemctl enable –now code-server@$USER
- Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml
- Configure CNAME, target group and application load balancer. Technically, you can use nginx & LetsEncrypt directly instead of using a load balancer, but it may be more work, and offloading SSL increases performance. Use /login as health check endpoint.


Troubleshooting: ALB thinks it’s unhealthy.
Try: curl -v http://0.0.0.0:8080/login and make sure it works. By using 0.0.0.0 instead of localhost, you check for the external bound IP address.
Tip: Install Iosevka or Fira Code on your OS, then set Editor Font & ligatures in user settings. (reference: issue #1374)
Install Custom App
To install lovia
app, you do it the “normal” (non-Kubernetes) way:
- bench get-app
- install app into desired site
e.g.
Switch to Remote AWS RDS MariaDB
- Edit common_site_config.json and add “db_host” and “rds_db”: 1. Optionally: “db_port”: 3306, “db_type”: “mariadb”.
- Make sure sites/SITE_NAME/site_config.json has proper db_name, db_password
- Purge local MariaDB:
You can still connect to MariaDB shell by using bench:
Updating ERPNext or Custom App
Before updating ERPNext/Frappe/custom app, it’s recommended to update bench CLI first.
To update just the “lovia
” custom app, you can do the command below. It will pull from app’s git repository, then run bench migrate.
To update all apps including frappe
and erpnext
, omit the “--apps lovia
“. It will update only from the current branch (same major version)
Example:
Increase Maximum Attachment File Upload Size
By default, the attachment file size limit is 5 MB. Increase this by editing frappe-bench/sites/SITE_NAME/site_config.json
and adding (for 100 MiB limit):
Reference: https://discuss.erpnext.com/t/maximum-file-size/4885/4?u=hendy
Troubleshooting: Sometimes 502 Bad Gateway
This happened since ERPNext v13.2.0 with no clear explanation why. Tailing by tail -f ~/frappe/bench/*.log doesn’t show any relevant messages. CPU utilization, free drive space, and memory utilization seems normal (even abundant).
/var/log/nginx/error.log does give errors:
If you’re thinking why server: erp.dirgatama.id but host is erp.lovia.life, it’s because the complete clause in nginx.conf is: server_name erp.dirgatama.id erp.lovia.life ;
So problem is not with ALB or nginx, but with web & socketio:
Check using curl:
Sometimes accessing via ELB gave 502 Bad Gateway, but curl to frappe-web or nginx works? erp CNAME has been checked to route directly to ALB.