Installing ERPNext 16 on Debian 13 with Virtualmin
By Eben van Deventer on October 29, 2026
Intermediate
ERPNext is an amazing, free and open-source ERP system built using the Frappe Framework. It is scalable, simple to use, and quite beautiful. It can, however, be challenging to install on an existing server that already hosts other applications.
In my case, I use Debian 13 with Virtualmin installed. This means MariaDB and Nginx are already serving several applications configured across various domains and subdomains. A conventional Frappe production installation could interfere with these services, so we need to configure Bench to work alongside Virtualmin using DNS multitenancy, Supervisor, and Virtualmin's existing Nginx configuration.
In this tutorial, we will work through the steps necessary to install Frappe Framework 16 and ERPNext 16 on an existing Debian 13 server managed by Virtualmin.
We will also install Python using uv, Node.js using NVM, and Frappe Bench using uv tools rather than the system Python package manager. Finally, we will cover several additional Frappe apps, including HRMS and Raven, together with maintenance, backup restoration, and troubleshooting.
Prerequisites
This how-to assumes that:
- Debian 13 (Trixie) is installed.
- Virtualmin is already configured and managing Nginx and MariaDB.
- You have root or equivalent administrative access.
- The relevant domain or subdomain points to your server.
- The corresponding Virtualmin virtual server has been created.
- SSL is configured through Virtualmin.
- You have a separate Unix user for the virtual server.
For this tutorial, we will use the following example configuration:
| SettingExample | |
| Domain | erpnext.example.com |
| Virtualmin Unix user | frappeuser |
| User home directory | /home/frappeuser |
| Bench name | frappe-bench |
| Bench directory | /home/frappeuser/frappe-bench |
| Frappe version | version-16 |
| Python | 3.14 |
| Node.js | 24 |
Replace these values with the appropriate details for your installation.
Do not use the Unix root account to install Bench or execute normal Bench commands. We will use the Virtualmin domain user for these operations and configure the required administrative permissions separately.
Software requirements
The principal software requirements for Frappe version 16 are:
| ComponentVersion | |
| Debian | 13 |
| Python | 3.14 |
| Node.js | 24 |
| MariaDB | 11.8 |
| Redis | 6 or later |
| Yarn | 1.22 or later |
| Frappe Framework | version-16 |
| ERPNext | version-16 |
The specific patch versions of Python, Node.js, npm and Bench may change over time.
Step 1 – Install prerequisite software packages
Start by logging into Debian as root or an administrative user.
Update the package repository and installed packages:
apt update && apt upgrade -y
Install the required dependencies:
apt install -y \build-essential \curl \wget \git \ca-certificates \pkg-config \software-properties-common \libssl-dev \zlib1g-dev \libbz2-dev \libreadline-dev \libsqlite3-dev \libffi-dev \liblzma-dev \libncurses-dev \libmariadb-dev \mariadb-client \redis-server \supervisor \cron \xvfb \libfontconfig1 \fontconfig \libjpeg-dev \libpng-dev
Because Virtualmin is already installed, MariaDB and Nginx should already be available. We do not want to replace their existing installations or configuration files.
Verify MariaDB:
mariadb --version
Verify Nginx:
nginx -v
Verify Redis and Supervisor:
systemctl status redis-serversystemctl status supervisor
Enable the required services:
systemctl enable --now redis-serversystemctl enable --now supervisorsystemctl enable --now cron
PDF generation
Frappe version 16 supports Chromium-based PDF generation, which is preferable for modern print formats.
We will install the required Chromium components through Bench after creating the bench instance:
bench setup-chrome
Older HTML print formats may still depend on wkhtmltopdf. If you use these, install a compatible, patched-Qt build separately after confirming its compatibility with Debian 13.
Do not install the old Debian 12 Bookworm .deb package without checking its dependencies.
Step 2 – Configure MariaDB
Configure MariaDB for use with Frappe Framework.
Open the MariaDB configuration file:
nano /etc/mysql/mariadb.conf.d/50-server.cnf
Under the existing [mysqld] block, ensure the following settings are present:
[mysqld]character-set-client-handshake = FALSEcharacter-set-server = utf8mb4collation-server = utf8mb4_unicode_ci
Do not create a second [mysqld] section unnecessarily.
Restart MariaDB:
systemctl restart mariadb
Verify the configuration:
mariadb -NBe \"SELECT VERSION(), @@character_set_server, @@collation_server;"
The old Barracuda InnoDB configuration is no longer required.
Note: Virtualmin may configure MariaDB administration through Unix socket authentication. The database account used when creating the Frappe site must have permission to create databases and database users.
Step 3 – Configure the Virtualmin user's sudo permissions
Instead of adding the Virtualmin user to the unrestricted sudo group, we will create a dedicated sudoers configuration for the administrative operations required by the bench.
The normal Bench commands will execute as the Virtualmin user, while service-management commands requiring elevated privileges will be executed through sudo.
Create the sudoers configuration
As root:
visudo -f /etc/sudoers.d/frappe-bench
Add the following, changing the username to match your Virtualmin user:
User_Alias FRAPPE_ADMIN = frappeuserCmnd_Alias FRAPPE_SUPERVISOR = \/usr/bin/supervisorctl status, \/usr/bin/supervisorctl rereadCmnd_Alias FRAPPE_NGINX = \/usr/sbin/nginx -t, \/usr/bin/systemctl reload nginxCmnd_Alias FRAPPE_SERVICES = \/usr/bin/systemctl status supervisor, \/usr/bin/systemctl status redis-server, \/usr/bin/systemctl status mariadbFRAPPE_ADMIN ALL=(root) \FRAPPE_SUPERVISOR, \FRAPPE_NGINX, \FRAPPE_SERVICES
The paths above are examples for Debian. Check executable locations using command -v and adjust if necessary.
We have deliberately not given the user unrestricted permission to run sudo bench or arbitrary Supervisor configuration updates. Bench can execute application code, and Supervisor can launch system processes, so these permissions can effectively provide root access if configured without sufficient restrictions.
An administrator should handle Supervisor configuration activation, or a separately reviewed, fixed-purpose helper can be used for automated deployments.
Save and exit.
Validate the sudoers configuration:
visudo -cf /etc/sudoers.d/frappe-bench
Switch to the Virtualmin user
su - frappeuser
Make sure you are in the user's home directory:
cd ~
Verify:
whoamiecho "$HOME"sudo -l
The remaining user-level installation steps must be performed as this Virtualmin user.
Step 4 – Install NVM and Node.js 24
Frappe version 16 requires Node.js 24.
We will use Node Version Manager (NVM) instead of Debian's system Node.js packages.
Install NVM:
curl -o- \https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh \| bash
Reload the shell:
source ~/.bashrc
Verify NVM:
command -v nvmnvm --version
Install Node.js 24:
nvm install 24nvm use 24nvm alias default 24
Verify Node.js and npm:
node -vnpm -v
The Node.js version should begin with v24..
Install Yarn Classic:
npm install -g yarn@1
Verify:
yarn --version
The Yarn version should begin with 1.22..
Find the Node.js executable:
which node
The path should resemble:
/home/frappeuser/.nvm/versions/node/v24.x.x/bin/node
Important: Make a note of this path. We will need to verify that Supervisor uses the correct NVM-managed Node executable for Frappe's Socket.IO service.
Step 5 – Install uv and Python 3.14
Frappe now recommends uv for installing Python and the Bench CLI.
Install uv:
curl -LsSf https://astral.sh/uv/install.sh | sh
Reload the shell:
source ~/.bashrc
Verify the installation:
uv --version
Install Python 3.14:
uv python install 3.14 --default
Confirm where uv installed Python:
uv python find 3.14
Verify the Python version:
uv run --no-project --python 3.14 \python --version
Expected result:
Python 3.14.x
Note that the exact Python patch version will depend on the latest available version at installation time.
Step 6 – Install Frappe Bench using uv tools
Previously, Frappe Bench was commonly installed using system pip.
For this installation, we will use uv tools instead:
uv tool install frappe-bench
Verify the Bench installation:
bench --version
Check the executable location:
which bench
The executable will normally be installed in:
/home/frappeuser/.local/bin/bench
If the command is not available in your shell, run:
uv tool update-shell
Then reopen your terminal session.
Do not use sudo pip install frappe-bench or Debian's --break-system-packages option.
Step 7 – Create a Frappe version-16 bench
Create a Bench instance for your ERPNext installation.
The bench name can be anything you like. For this tutorial, we will use frappe-bench.
Make sure you are in the user's home directory:
cd ~
Initialize the bench using Frappe Framework version 16 and Python 3.14:
bench init \--frappe-branch version-16 \--python "$(uv python find 3.14)" \frappe-bench
This creates the bench directory, downloads Frappe Framework, and creates its Python virtual environment.
Change to the new bench directory:
cd ~/frappe-bench
Verify the Python version inside the environment:
./env/bin/python --version
Expected:
Python 3.14.x
Verify the installed Frappe version:
bench version
Configure DNS multitenancy
Since Nginx is already managed by Virtualmin and may serve multiple domains, enable DNS multitenancy:
bench config dns_multitenant on
This ensures incoming requests are matched to the appropriate Frappe site using their hostname.
Install Chromium for PDF generation
bench setup-chrome
This downloads the Chromium browser used by Frappe's modern PDF generation engine.
Step 8 – Download ERPNext and additional Frappe apps
We will now download the applications that we intend to install into our Frappe site.
At a minimum, ERPNext is required for this tutorial.
ERPNext
bench get-app --branch version-16 erpnext
Frappe HRMS
Frappe HRMS provides Human Resource and Payroll Management.
bench get-app --branch version-16 hrms
Raven
Raven provides team messaging, channels, notifications, and collaboration within the Frappe ecosystem.
Its stable main branch supports Frappe version 16.
bench get-app --branch main raven
Other optional applications
The following applications may also be useful, depending on the functionality you require.
Only install applications whose current branches and dependencies have been verified against Frappe version 16.
Frappe Offsite Backups
Repository:
https://github.com/frappe/offsite_backups
bench get-app --branch version-16 offsite_backups
Frappe Blog
Repository:
https://github.com/frappe/blog
bench get-app --branch version-16 blog
Frappe WhatsApp
Repository:
https://github.com/shridarpatil/frappe_whatsapp
bench get-app https://github.com/shridarpatil/frappe_whatsapp
Industrial Relations
Repository:
bench get-app --branch version-16 https://github.com/buff0k/ir
Other custom applications can be added using their actual Git repository URLs.
For example:
bench get-app \https://github.com/OWNER/REPOSITORY.git
Avoid assuming older repositories have retained their historical Git URLs or support Frappe version 16.
Note: Downloading an app with bench get-app does not automatically install it into a site.
Step 9 – Create a Frappe site
We will now create the site that will host ERPNext.
Use the fully qualified domain name configured in Virtualmin.
In this example:
erpnext.example.com
Make sure you are inside your bench directory:
cd ~/frappe-bench
Enable the venv:
source env/bin/activate
Create the site:
bench new-site erpnext.example.com
The installer will request database administrative credentials and an ERPNext Administrator password.
Use the database account authorized to provision Frappe databases.
If Virtualmin uses socket authentication for the MariaDB root account, you may need to provision a separate database administrative account with the necessary privileges.
During the installation, you will be asked to create an Administrator password.
Make a note of this password, as it will be required when logging in to ERPNext.
After installation, confirm the site directory exists:
ls -la sites/
You should see a directory corresponding to the domain name.
Step 10 – Install ERPNext and additional apps into the site
Install ERPNext:
bench --site erpnext.example.com \install-app erpnext
Install HRMS:
bench --site erpnext.example.com \install-app hrms
Install Raven:
bench --site erpnext.example.com \install-app raven
Optional applications can be installed using the same command, substituting the appropriate application name.
For example:
bench --site erpnext.example.com \install-app ir
Verify the installed applications:
bench --site erpnext.example.com \list-apps
Enable the scheduler:
bench --site erpnext.example.com \enable-scheduler
Migrate the site:
bench --site erpnext.example.com \migrate
Build the application assets:
bench build
At this point, the applications have been installed, but we still need to configure the production services.
Step 11 – Configure Bench for production using Supervisor
Normally, Frappe recommends running:
sudo bench setup production USERNAME
However, we will not use this command because Virtualmin already manages Nginx and other server services.
Instead, we will configure Supervisor separately and integrate the web application into Virtualmin's existing Nginx configuration.
Generate Supervisor configuration
From the bench directory:
cd ~/frappe-bench
Generate Supervisor configuration:
bench setup supervisor
This creates:
/home/frappeuser/frappe-bench/config/supervisor.conf
Verify the Node.js executable
Open the generated configuration:
nano config/supervisor.conf
Find the websocket program, which should resemble:
[program:frappe-bench-websocket]
Ensure its command points to the NVM-managed Node.js executable.
For example:
[program:frappe-bench-websocket]command=/home/frappeuser/.nvm/versions/node/v24.x.x/bin/node /home/frappeuser/frappe-bench/apps/frappe/socketio.jsdirectory=/home/frappeuser/frappe-benchautostart=trueautorestart=truestderr_logfile=/home/frappeuser/frappe-bench/logs/websocket.error.logstdout_logfile=/home/frappeuser/frappe-bench/logs/websocket.log
Replace v24.x.x with the exact directory returned by:
which node
Do not paste the example version literally.
Preserve the remaining generated Supervisor settings, including environment variables, process users, groups, and worker configuration.
Register the configuration with Supervisor
The following commands require an administrator.
Create a symbolic link to the generated configuration:
ln -s \/home/frappeuser/frappe-bench/config/supervisor.conf \/etc/supervisor/conf.d/frappe-bench.conf
If the file already exists, inspect it before replacing it.
Reload Supervisor:
supervisorctl rereadsupervisorctl update
Check the process status:
supervisorctl status
You should see the processes belonging to frappe-bench.
Do not use:
supervisorctl stop all
or:
supervisorctl restart all
On a Virtualmin server hosting multiple benches, these commands could interrupt unrelated applications.
Use the actual Supervisor program groups shown by supervisorctl status when restarting or diagnosing a specific bench.
Security note: The administrator must review the generated Supervisor configuration before activation. A privileged Supervisor service should not blindly reload untrusted configuration files controlled by an unprivileged user.
Step 12 – Configure Nginx to work with Virtualmin
This is one of the most important steps.
Virtualmin already manages Nginx and is responsible for the domains, SSL certificates, and other web applications installed on the server.
We therefore want to avoid deploying Bench's Nginx configuration as a separate virtual host.
Instead, we will configure Virtualmin's existing domain to reverse-proxy requests to the Frappe web server and Socket.IO service.
Identify the Bench ports
Inspect:
cat sites/common_site_config.json
Identify the configured ports, particularly:
webserver_portsocketio_port- Redis-related port settings
The examples below assume the web server uses port 8000 and Socket.IO uses port 9000.
These are examples only. Use the ports assigned to your bench.
Open the Virtualmin Nginx configuration
In Virtualmin, select the appropriate virtual server and edit its Nginx configuration.
The configuration is typically stored in a file under:
/etc/nginx/sites-available/
The filename depends on how Virtualmin configured the virtual server.
Make a backup before modifying it.
Configure Frappe proxy locations
The following example shows the required directives to be defined before the existing Virtualmin server block.
upstream frappe-bench-frappe {server 127.0.0.1:8000 fail_timeout=0;}upstream frappe-bench-socketio-server {server 127.0.0.1:9000 fail_timeout=0;}
The following example shows the required directives to integrate into the existing Virtualmin HTTPS server block.
Do not create a duplicate server block, duplicate existing location definitions, or replace Virtualmin's SSL directives.
proxy_buffer_size 128k;proxy_buffers 4 256k;proxy_busy_buffers_size 256k;add_header X-Frame-Options "SAMEORIGIN";add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";add_header X-Content-Type-Options nosniff;add_header X-XSS-Protection "1; mode=block";add_header Referrer-Policy "same-origin, strict-origin-when-cross-origin";location /assets {try_files $uri =404;add_header Cache-Control "max-age=31536000";}location ~ ^/protected/(.*) {internal;try_files /$host/$1 =404;}location /socket.io {proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_set_header X-Frappe-Site-Name $host;proxy_set_header Origin $scheme://$http_host;proxy_set_header Host $host;proxy_pass http://frappe-bench-socketio-server;}location / {rewrite ^(.+)/$ $1 permanent;rewrite ^(.+)/index\.html$ $1 permanent;rewrite ^(.+)\.html$ $1 permanent;location ~* ^/files/.*.(htm|html|svg|xml) {add_header Content-disposition "attachment";try_files /$host/public/$uri @webserver;}try_files /$host/public/$uri @webserver;}location @webserver {proxy_http_version 1.1;proxy_set_header X-Forwarded-For $remote_addr;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-Frappe-Site-Name $host;proxy_set_header Host $host;proxy_set_header X-Use-X-Accel-Redirect True;proxy_read_timeout 120;proxy_redirect off;proxy_pass http://frappe-bench-frappe;}# error pageserror_page 502 /502.html;location /502.html {root /usr/local/lib/python3.11/dist-packages/bench/config/templates;internal;}# optimizationssendfile on;keepalive_timeout 15;client_max_body_size 50m;client_body_buffer_size 16K;client_header_buffer_size 1k;# enable gzip compresion# based on https://mattstauffer.co/blog/enabling-gzip-on-nginx-servers-including-laravel-forgegzip on;gzip_http_version 1.1;gzip_comp_level 5;gzip_min_length 256;gzip_proxied any;gzip_vary on;gzip_typesapplication/atom+xmlapplication/javascriptapplication/jsonapplication/rss+xmlapplication/vnd.ms-fontobjectapplication/x-font-ttfapplication/font-woffapplication/x-web-app-manifest+jsonapplication/xhtml+xmlapplication/xmlfont/opentypeimage/svg+xmlimage/x-icontext/csstext/plaintext/x-component;
Replace the username, bench path and port numbers with those applicable to your installation.
This example proxies public file requests through Frappe instead of attempting to serve them directly from Nginx. It also includes the internal protected-file location required for Frappe's accelerated private-file responses.
Compare this configuration with the file generated by:
bench setup nginx
The generated configuration can be inspected at:
config/nginx.conf
Use it as the reference for any version-specific headers, private-file handling, or additional routes required by the installed Frappe release.
Important: Do not link the generated Bench Nginx configuration into /etc/nginx/conf.d/ on an existing Virtualmin installation. The Virtualmin virtual server must remain authoritative for its domain and SSL configuration.
File permissions
Nginx must have sufficient permissions to traverse the user's home directory and read the generated public assets.
Avoid changing the home directory to world-readable or world-traversable without considering the security impact.
Where necessary, use targeted filesystem ACLs to grant the Nginx worker access to the required paths.
Validate Nginx
As root or an authorized administrator:
nginx -t
If the configuration is valid:
systemctl reload nginx
Reloading Nginx is preferable to restarting it unnecessarily because other Virtualmin websites may be active.
Step 13 – Verify the installation
Confirm the Frappe processes are running:
sudo supervisorctl status
Verify that the configured local web port responds:
curl -I http://127.0.0.1:8000
This only confirms that the local web service responds; the Frappe site still depends on the correct hostname being supplied.
Test with the actual domain:
curl -I \-H "Host: erpnext.example.com" \http://127.0.0.1:8000
Test the Virtualmin HTTPS endpoint:
curl -I https://erpnext.example.com
Finally, open your browser and navigate to:
https://erpnext.example.com
You should see the Frappe / ERPNext login page.
Log in using:
Username: AdministratorPassword: [The password created during site installation]
If you have installed Raven, verify that its interface and required assets load correctly.
Step 14 – Updating your Bench and applications
From time to time, you will need to update Frappe Framework, ERPNext, and any additional applications.
Log in as the Virtualmin user and enter the bench directory:
cd ~/frappe-bench
Create a backup first:
bench --site erpnext.example.com \backup --with-files
Review local Git changes before updating:
git -C apps/frappe status --shortgit -C apps/erpnext status --short
Run the normal update:
bench update
This handles the normal application update process, including patches and asset compilation.
Avoid using --reset as part of routine updates where local application changes may exist.
Manual update steps
If the automatic update fails, it may be necessary to execute individual operations:
bench setup requirementsbench buildbench --site erpnext.example.com migrate
Restart the bench:
bench restart
If Supervisor permissions prevent Bench from restarting, have the administrator restart only the appropriate bench process groups.
Updating the Bench CLI
The Bench CLI was installed using uv tools and should be updated using the same mechanism:
uv tool upgrade frappe-bench
Verify:
bench --version
Do not update Bench using system pip.
Step 15 – Migrating an existing bench to Python 3.14
This section is intended for existing Frappe installations being upgraded rather than new installations.
Before upgrading a production installation:
- Take a complete database and file backup.
- Verify that all installed apps support Frappe version 16.
- Review migration requirements between major Frappe versions.
- Test the migration on a staging bench.
- Confirm that Node.js 24 and Python 3.14 are available.
Changing Git branches alone is not a complete major-version migration procedure.
Where the installed applications explicitly support version 16, the corresponding branch-switch operation may resemble:
bench switch-to-branch version-16 \frappe erpnext hrms \--upgrade
Only include apps that are installed and have the relevant branch.
Raven may remain on its supported main branch.
Migrate the Python environment
bench migrate-env \"$(uv python find 3.14)"
Verify:
./env/bin/python --version
Expected:
Python 3.14.x
Reinstall requirements and rebuild assets:
bench setup requirementsbench build
Run database migrations:
bench --site erpnext.example.com migrate
Restart the bench:
bench restart
If the environment reports missing Python packaging components, inspect the specific error before applying a workaround.
For errors explicitly involving missing setuptools or wheel in the bench environment, you may use:
uv pip install \--python ./env/bin/python \-U setuptools wheel
Do not routinely install additional packages into the environment unless required.
Step 16 – Restoring from backups
Frappe backups generally consist of the database and associated site files.
The typical backup files include:
...-database.sql.gz– Database backup....-files.taror...-files.tar.gz– Public files....-private-files.taror...-private-files.tar.gz– Private files.
Make sure the backup files are available to the Virtualmin user.
For this example, we will assume the files have been placed in /tmp.
Enter the bench directory:
cd ~/frappe-bench
Restore:
bench --site erpnext.example.com \--force restore \/tmp/database.sql.gz \--with-private-files /tmp/private-files.tar \--with-public-files /tmp/files.tar
Replace the filenames with the actual backup filenames.
Warning: This operation replaces data in the target site's database. Confirm that the target site and backup are correct before proceeding.
The site configuration, encryption key, installed applications and application versions must also be compatible with the restored backup.
After restoring:
bench --site erpnext.example.com migratebench buildbench restart
Step 17 – Reinstalling a site from scratch
If you are testing or want to reset a site's database without rebuilding the entire bench, use the reinstall command.
Enter the bench directory:
cd ~/frappe-bench
Execute:
bench --site erpnext.example.com \--force reinstall
Warning: This is destructive and removes the existing site database contents. Do not use it on a production site unless you intend to discard its data and have verified your backups.
Step 18 – Troubleshooting Redis issues
Frappe uses Redis for caching, background jobs, and other internal services.
Each bench normally maintains its own Redis configuration, with separate ports for caching and queue processing.
Inspect:
cat sites/common_site_config.json
The configuration should contain the Redis connection URLs used by the bench.
If Redis is not starting correctly, inspect the generated configuration files:
ls -la config/redis*
Regenerate the Bench Redis configuration where appropriate:
bench setup redis
Regenerate Supervisor configuration if Redis process definitions must be updated:
bench setup supervisor
An administrator must review and reload the changed Supervisor configuration.
Avoid arbitrarily pointing every Redis service to the system Redis instance on port 6379.
Although this may appear to fix connectivity, sharing Redis databases and namespaces incorrectly can introduce collisions between benches and unexpected behaviour.
Step 19 – Running multiple benches on the same Virtualmin server
One of the main advantages of this configuration is that multiple benches can run on the same Debian 13 server.
Each bench can belong to a separate Virtualmin Unix user and be configured for its own domains.
However, you must ensure that the services do not conflict.
Each bench must have its own:
- Bench directory.
- Unix user and appropriate permissions.
- Supervisor configuration.
- Web server port.
- Socket.IO port.
- Redis service ports.
- Virtualmin Nginx proxy configuration.
Inspect the configuration:
cat sites/common_site_config.json
If you need to change the ports, use Bench's supported configuration commands where available.
After changing Redis ports, regenerate the configuration:
bench setup redis
Regenerate Supervisor configuration:
bench setup supervisor
Verify that no two benches are attempting to bind to the same ports.
When using NVM, also ensure that each bench's Socket.IO process uses the correct Node.js executable belonging to its respective Unix user.
Do not use a shared global Node installation or modify the system Node executable to fix an individual bench.
Step 20 – Configure developer mode (optional)
If you intend to develop Frappe applications on this bench, enable developer mode.
From the bench directory:
bench set-config -g developer_mode 1
Install developer requirements:
bench setup requirements --dev
Developer mode is intended for development environments and should generally remain disabled on production installations.
To disable it:
bench set-config -g developer_mode 0
Step 21 – Useful maintenance and troubleshooting commands
The following commands are useful for ongoing maintenance.
Check installed applications
bench --site erpnext.example.com list-apps
Check Frappe versions
bench version
Check the Python environment
./env/bin/python --version
Check Node.js
node -vwhich node
Check Supervisor
sudo supervisorctl status
Inspect bench configuration
cat sites/common_site_config.json
Inspect bench logs
ls -lh logs/tail -n 100 logs/web.error.log
Migrate a site
bench --site erpnext.example.com migrate
Rebuild assets
bench build
Clear cache
bench --site erpnext.example.com clear-cache
Clear website cache
bench --site erpnext.example.com clear-website-cache
Back up the site
bench --site erpnext.example.com \backup --with-files
Update the Bench CLI
uv tool upgrade frappe-bench
Validate Nginx
sudo nginx -t
Conclusion
You should now have a functioning Frappe Framework 16 and ERPNext 16 installation running on Debian 13, with Virtualmin continuing to manage your domain configuration, SSL certificates, MariaDB and Nginx.
The Frappe processes are managed separately through Supervisor, while Node.js is installed through NVM, Python is managed by uv, and Frappe Bench is installed using uv tools.
This architecture is particularly useful for existing Virtualmin servers that host several web applications or multiple Frappe benches, without requiring a dedicated server or replacing the existing web-server configuration.
Once the installation is operational, you can continue adding compatible Frappe applications, including HRMS, Raven and your own custom apps.
No comments yet. Login to start a new discussion Start a new discussion