Skip to content
Alignd

Upgrades

Upgrading ERPNext from v15 to v16: a practical checklist

Frappe and ERPNext version 16 came out on 12 January 2026. The upgrade itself only takes a few commands, but most of the work happens around them: you need a new runtime, some features have moved out of the framework into their own apps, and your custom code has to be checked.

By Asif Mulani, who built Frappe Sheets · Published

What changes between v15 and v16

The full lists are in the Frappe v16.0.0 and ERPNext v16.0.0 release notes. These are the changes you'll need to plan for.

the only Python version Frappe v16 accepts
3.14
the only Python version Frappe v16 accepts
the lowest Node version v16 runs on
24
the lowest Node version v16 runs on
features moved out of the framework into their own apps
4
features moved out of the framework into their own apps

A new runtime

Current v15 releases accept Python 3.10 to 3.14 and Node 18 or later; v16 accepts only Python 3.14 and needs Node 24 or later. These come from requires-python in Frappe's pyproject.toml and engines in its package.json. On most servers that means installing Python 3.14 and Node 24 before you do anything else, and you can't reuse your current virtualenv as it is.

Four features moved out of the framework

These four are no longer in the v16 framework code and now live in separate apps. A v16 patch prints a warning for each one during migrate:

  • Newsletter, now frappe/newsletter
  • Blogs, now frappe/blog
  • Offsite backups to Google Drive, S3 and Dropbox, now frappe/offsite_backups
  • The Social module and energy points, now frappe/eps

If your site uses any of them, add the app to the new bench before you migrate. Offsite backups is the one people miss, because nobody looks at it until the day they need a backup.

Desk navigation

v16 adds workspace sidebars and desktop icons, and ERPNext ships them for its modules. It's the first thing your users will notice. If you've built custom workspaces, check how they appear in the new sidebar before training anyone.

Settings that moved

Go through the ERPNext release notes for any setting you rely on. For example, the default manufacturing warehouses have moved from Manufacturing Settings to Company, so each company now sets its own, and Print Settings has a new PDF Generator option where you choose Chrome or wkhtmltopdf.

Before you start

  1. List every app on the bench with its branch. Each one needs a version-16 branch or release: ERPNext, HRMS, India Compliance, payment apps, and every custom app.
  2. Check custom apps for Python 3.14. Run their tests on 3.14 before you touch the framework version, and include the third-party packages they pull in.
  3. Read the deprecation warnings. Frappe v16 has a deprecation_dumpster.py that records, for each deprecated function, the version in which it becomes an error. ERPNext added the same mechanism. Run your custom app on the latest v15 with warnings visible and fix what shows up.
  4. Declare the dependency. ERPNext and HRMS v16 declare frappe = ">=16.0.0,<17.0.0" under [tool.bench.frappe-dependencies] in pyproject.toml. Do the same in your custom app so it can't be installed against the wrong framework.
  5. Check customisations as well as code. Custom fields, property setters, client scripts and server scripts are site data, so they survive the upgrade, but they can still break if they refer to a field that v16 renamed or moved.

Take a backup you can actually restore

You only know a backup works once you've restored it. Take it with files:

bench --site erp.example.com backup --with-files

This writes the database dump, the public and private file archives, and a copy of the site config. Copy them off the server, then restore them on another machine and open the site. That restored copy becomes your staging site.

bench --site staging.example.com restore database.sql.gz \
  --with-public-files files.tar \
  --with-private-files private-files.tar

If Encrypt Backups is on in System Settings, keep the backup_encryption_key from site_config.json with the files. Without that key you can't decrypt the backup, so it's no use to you.

Upgrade on staging first

Because the Python version changes, the cleanest route is to build a new v16 bench next to the old one instead of switching in place. The old bench stays untouched, and that's your rollback.

bench init erp16 --frappe-branch version-16 --python python3.14
cd erp16
bench get-app erpnext --branch version-16
bench get-app hrms --branch version-16
# plus India Compliance and each custom app, on their version-16 branches

Restore the v15 backup into a new site on this bench, then run bench --site staging.example.com migrate. Migrate runs in a fixed order: before_migrate hooks, patches marked to run before the schema sync, the DocType schema sync, the remaining patches, then fixtures, customisations, scheduled job sync and after_migrate hooks.

If a patch fails, fix the cause and run migrate again. migrate has a --skip-failing flag. Don't use it on a site you plan to keep, because a skipped patch leaves data in a state the next version doesn't expect.

An in-place upgrade also exists: bench switch-to-branch version-16 --upgrade, followed by bench update. It works, but you still need Python 3.14 in the virtualenv first (bench migrate-env python3.14), and you lose the clean side-by-side rollback.

Test what the business actually does

Clicking around Desk won't tell you much. Walk through full cycles on staging with real data:

  • Quotation to sales order to delivery note to sales invoice to payment entry.
  • Purchase order to purchase receipt to purchase invoice.
  • A stock entry and a stock reconciliation, then the stock ledger and stock balance reports.
  • Trial balance, profit and loss and balance sheet for the last closed month, compared with v15.
  • A payroll run, if HRMS is installed, compared slip by slip.
  • GST: e-invoice and e-way bill in sandbox mode, if India Compliance is installed.
  • Every print format that leaves the building: invoice, delivery note, purchase order, payslip.
  • Scheduled jobs, email sending and any integration that calls in or out.

Matching last month's trial balance to the paisa on both versions is the single most useful check. If the numbers move, something in a patch or a custom report needs attention.

Go-live and rollback

  1. Agree a window outside business hours and freeze changes on v15.
  2. Take a final backup with files and copy it off the server.
  3. Restore it onto the v16 bench and run migrate, exactly as rehearsed on staging.
  4. Run the same short test script on production before users log in.
  5. Keep the v15 bench and the final backup until the first month closes cleanly on v16.

Rolling back means pointing traffic back to the v15 bench and restoring the pre-upgrade backup there. There is no downgrade path for a migrated database, and Frappe's restore warns when a backup comes from a newer version. Anything entered on v16 after go-live has to be re-entered, which is why you run the test script before users log in.

Common questions

We don't skip a version. Upgrade to v15, check the site, then go to v16. If you jump straight across, two versions' worth of patches run in one go and you get no chance to check anything in between.

Not by switching branches, because patches only change the database in one direction. To roll back, you restore the backup you took on v15 onto a v15 bench. Frappe warns you if you try to restore a newer backup onto an older version.

Yes. Frappe v16 declares requires-python >=3.14,<3.15 in its pyproject.toml, and ERPNext v16 requires Python 3.14 or later. Recent v15 releases also accept 3.14, so you can change the runtime first and upgrade the code later.

Let's start with a call.

Thirty minutes, free of charge, and you won't sit through a sales deck.