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
Domainerpnext.example.com
Virtualmin Unix userfrappeuser
User home directory/home/frappeuser
Bench namefrappe-bench
Bench directory/home/frappeuser/frappe-bench
Frappe versionversion-16
Python3.14
Node.js24

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
Debian13
Python3.14
Node.js24
MariaDB11.8
Redis6 or later
Yarn1.22 or later
Frappe Frameworkversion-16
ERPNextversion-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-server
systemctl status supervisor

Enable the required services:

systemctl enable --now redis-server
systemctl enable --now supervisor
systemctl 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 = FALSE
character-set-server = utf8mb4
collation-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 = frappeuser

Cmnd_Alias FRAPPE_SUPERVISOR = \
   /usr/bin/supervisorctl status, \
   /usr/bin/supervisorctl reread

Cmnd_Alias FRAPPE_NGINX = \
   /usr/sbin/nginx -t, \
   /usr/bin/systemctl reload nginx

Cmnd_Alias FRAPPE_SERVICES = \
   /usr/bin/systemctl status supervisor, \
   /usr/bin/systemctl status redis-server, \
   /usr/bin/systemctl status mariadb

FRAPPE_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:

whoami
echo "$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 nvm
nvm --version

Install Node.js 24:

nvm install 24
nvm use 24
nvm alias default 24

Verify Node.js and npm:

node -v
npm -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:

https://github.com/buff0k/ir

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.js
directory=/home/frappeuser/frappe-bench
autostart=true
autorestart=true
stderr_logfile=/home/frappeuser/frappe-bench/logs/websocket.error.log
stdout_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 reread
supervisorctl 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_port
  • socketio_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 pages
error_page 502 /502.html;
location /502.html {
     root /usr/local/lib/python3.11/dist-packages/bench/config/templates;
     internal;
  }


# optimizations
sendfile 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-forge
gzip on;
gzip_http_version 1.1;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
     application/atom+xml
     application/javascript
     application/json
     application/rss+xml
     application/vnd.ms-fontobject
     application/x-font-ttf
     application/font-woff
     application/x-web-app-manifest+json
     application/xhtml+xml
     application/xml
     font/opentype
     image/svg+xml
     image/x-icon
     text/css
     text/plain
     text/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: Administrator
Password: [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 --short
git -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 requirements
bench build
bench --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:

  1. Take a complete database and file backup.
  2. Verify that all installed apps support Frappe version 16.
  3. Review migration requirements between major Frappe versions.
  4. Test the migration on a staging bench.
  5. 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 requirements
bench 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.tar or ...-files.tar.gz – Public files.
  • ...-private-files.tar or ...-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 migrate
bench build
bench 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 -v
which 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.

References


More articles on ERPNext


Comments

No comments yet.

Add a comment
Ctrl+Enter to add comment