frappe-ops-upgrades

Installation
SKILL.md

Version Upgrades

Complete guide for upgrading Frappe/ERPNext between major versions, handling failed migrations, and rolling back safely.

Versions: v14 → v15 → v16


Quick Reference: Upgrade Commands

Task Command
Full update bench update
Update specific app bench update --pull --app erpnext
Switch branch bench switch-to-branch version-15 frappe erpnext
Run migrations only bench --site mysite migrate
Check migration readiness bench --site mysite ready-for-migration
Backup before upgrade bench --site mysite backup
Restore from backup bench --site mysite restore /path/to/backup.sql.gz
Re-run failed patch Add #YYYY-MM-DD suffix in patches.txt

Decision Tree: Upgrade Strategy

Need to upgrade?
├── Single minor version bump (e.g., v15.10 → v15.20)?
│   └── YES → Run `bench update` directly
├── Major version jump (e.g., v14 → v15)?
│   ├── Have custom apps?
│   │   ├── YES → Test on staging FIRST, check breaking changes
│   │   └── NO → Follow standard upgrade path
│   └── Multiple major versions (v14 → v16)?
│       └── ALWAYS upgrade one version at a time: v14 → v15 → v16
└── Production environment?
    ├── YES → ALWAYS test on staging clone first
    └── NO → Proceed with standard upgrade

Pre-Upgrade Checklist

ALWAYS complete these steps before ANY major version upgrade:

  1. Full backupbench --site mysite backup --with-files
  2. Test on staging — Clone production to a staging bench and test there first
  3. Check breaking changes — Review the breaking changes section below
  4. Audit custom apps — Run custom apps against new version's API changes
  5. Check Python/Node versions — v15 requires Node 18+; v16 requires Node 24+, Python 3.14+
  6. Disable schedulerbench --site mysite scheduler disable
  7. Check pending jobsbench --site mysite ready-for-migration
  8. Read release notes — Check GitHub release notes for each version

Standard Upgrade Process

Step-by-Step

# 1. Backup all sites
bench backup-all-sites

# 2. Switch to target version branch
bench switch-to-branch version-15 frappe erpnext

# 3. Update (pulls code, installs deps, builds, migrates)
bench update

# 4. Verify
bench --site mysite migrate  # if not done by update
bench version                # confirm versions

What bench update Executes (In Order)

  1. Backup all sites
  2. Pull latest code for all apps (git pull)
  3. Install Python requirements (pip install)
  4. Install Node requirements (yarn install)
  5. Build static assets (bench build)
  6. Run migrations on all sites (bench migrate)
  7. Restart bench processes

v14 → v15 Breaking Changes

Environment Requirements

Requirement v14 v15
Node.js v14+ v18+
Python packaging setup.py pyproject.toml

Backend Breaking Changes

  • db.set() removed — Use doc.db_set() instead
  • db.sql() parameters removedas_utf8 and formatted no longer accepted
  • db.set_value() for Singles — Use frappe.db.set_single_value() instead
  • job_name deprecated — Use job_id parameter in enqueue()
  • frappe.new_doc() argumentsparent_doc, parentfield, as_dict MUST be keyword args
  • frappe.get_installed_apps() — No longer accepts sort or frappe_last args
  • Method override order reversed — Last override now takes precedence
  • Timezone functions renamedconvert_utc_to_user_timezoneconvert_utc_to_system_timezone

Frontend Breaking Changes

  • Vue 2 → Vue 3 — All Vue components MUST be migrated
  • Window globals removedget_todayfrappe.datetime.get_today, userfrappe.session.user
  • this in Client Scripts — Local scope access no longer supported
  • Image lazy loading — Replace website-image-lazy class with native loading="lazy"

Security Changes

  • Server Scripts disabled by default — Enable: bench set-config -g server_script_enabled 1
  • "Desk User" role added — Replaces "All" role for desk user permissions
  • currentsite.txt removed — Use bench use sitename or FRAPPE_SITE env var

Removed Features

  • Event Streaming moved to separate app
  • Cordova support removed
  • setup.py removed (use pyproject.toml)
  • --make_copy and --restore build flags removed (use --hard-link)

See breaking-changes.md for the complete list.


v15 → v16 Breaking Changes

Environment Requirements

Requirement v15 v16
Node.js v18+ v24+
Python 3.10+ 3.14+

Backend Breaking Changes

  • Default sort order changedcreation instead of modified for all list queries
  • has_permission hooks — MUST return explicit True; None no longer accepted
  • frappe.get_doc(doctype, name, field=value) — No longer updates values
  • DB commits in document hooks — No longer allowed to prevent data integrity issues
  • frappe.sendmail(now=True) — No longer commits transactions implicitly
  • db.get_value() for Singles — Now returns proper types instead of strings
  • State-changing methods require POST/api/method/logout, /api/method/upload_file, etc.

Separated Modules (Install Separately)

  • Energy Points → frappe/eps
  • Newsletter → frappe/newsletter
  • Backup Integrations → frappe/offsite_backups
  • Blog → frappe/blog

Frontend Breaking Changes

  • Report/Dashboard/Page JS evaluated as IIFEs (no global scope pollution)
  • Awesome Bar redesigned, moved to sidebar (Cmd+K)
  • List view right sidebar removed
  • /apps endpoint deprecated; /app reroutes to /desk

Configuration Changes

  • Site config cached for up to one minute (changes not immediate)
  • Country field requires valid ISO 3166 ALPHA-2 code
  • bench version output format changed to "plain" (use -f legacy for old format)
  • override_doctype hook classes MUST inherit from the overridden class

See breaking-changes.md for the complete list.


Patch System

How Patches Work

Patches are one-off data migration scripts that run during bench migrate. They are defined in each app's patches.txt file.

patches.txt Format [v14+]

[pre_model_sync]
# Runs BEFORE schema sync — use for data prep
myapp.patches.v15_0.prepare_data_for_migration

[post_model_sync]
# Runs AFTER schema sync — use for data that needs new schema
myapp.patches.v15_0.migrate_data_to_new_fields

Patch Execution Rules

  • Patches run in the order defined in patches.txt
  • Each patch runs exactly ONCE — tracked in the __patches table
  • To re-run a patch, append a date comment: myapp.patches.v15_0.fix #2025-03-20
  • One-off statements: execute:frappe.delete_doc('Page', 'old_page', ignore_missing=True)

Writing a Patch

# myapp/patches/v15_0/migrate_field_data.py
import frappe

def execute():
    # ALWAYS reload if you need the NEW schema
    frappe.reload_doc("module_name", "doctype", "doctype_name")

    # Perform data migration
    frappe.db.sql("""
        UPDATE `tabSales Invoice`
        SET new_field = old_field
        WHERE old_field IS NOT NULL
    """)

Debugging Stuck Patches

# Check which patches have run
bench --site mysite console
>>> frappe.db.sql("SELECT * FROM __patches WHERE patch LIKE '%stuck_patch%'")

# Remove a patch record to force re-run
>>> frappe.db.sql("DELETE FROM __patches WHERE patch = 'myapp.patches.v15_0.broken_patch'")
>>> frappe.db.commit()

# Then re-run migrate
bench --site mysite migrate

Rollback Procedure

Immediate Rollback (Within Hours)

# 1. Stop all processes
bench stop

# 2. Restore database from pre-upgrade backup
bench --site mysite restore /path/to/pre-upgrade-backup.sql.gz \
  --with-public-files /path/to/files.tar \
  --with-private-files /path/to/private-files.tar

# 3. Switch back to previous version branch
bench switch-to-branch version-14 frappe erpnext

# 4. Install old dependencies
bench setup requirements

# 5. Build old assets
bench build

# 6. Start bench
bench start  # or: sudo bench restart (production)

Critical Rules for Rollback

  • ALWAYS keep pre-upgrade backups for at least 7 days
  • NEVER run bench migrate after restoring to old branch — schema is already correct
  • ALWAYS restore files alongside database — file references may break otherwise
  • NEVER attempt rollback after users have created new data on the upgraded version

Frappe Packages: Moving Customizations Between Sites

Frappe Packages (v14+) are lightweight UI-built applications — bundles of Custom Module Defs distributed as .tar.gz tarballs. For Custom Fields, Property Setters, and DocPerms on standard DocTypes, use Fixtures instead.

Quick Reference: Package vs Fixtures vs App

Mechanism Use When CLI Command
Package UI-built DocTypes, Scripts, Web Pages UI only (Package Import/Release)
Fixtures Custom Fields, Property Setters, DocPerms bench --site mysite export-fixtures
Frappe App Full development workflow, CI/CD, tests bench get-app, bench install-app

Package Workflow (UI-Based)

  1. Create a Package document → assign Custom Module Defs to it
  2. Create a Package Release → exports to [bench]/sites/[site]/packages/ as [package]-[version].tar.gz
  3. On target site, create Package Import → attach tarball, check Activate
  4. System migrates data like an app migration; use Force to overwrite existing files

Fixtures Workflow (CLI-Based)

# hooks.py — define what to export
fixtures = [
    "Custom Field",
    "Property Setter",
    {"dt": "Client Script", "filters": [["module", "=", "My Module"]]}
]
# Export fixtures to JSON in your app
bench --site mysite export-fixtures --app myapp

# Fixtures auto-sync on: bench --site mysite migrate

NEVER use Packages to modify standard/core DocTypes — use a Frappe App with Fixtures.

See frappe-packages.md for the complete reference including decision trees, limitations, and best practices.


Custom App Compatibility Checks

Before upgrading, audit each custom app:

  1. Check deprecated APIs — Search for removed functions listed in breaking changes
  2. Check setup.py — Must migrate to pyproject.toml for v15+
  3. Check Vue components — Must be Vue 3 compatible for v15+
  4. Check patches.txt — Ensure patches use [pre_model_sync]/[post_model_sync] sections [v14+]
  5. Check hooks.py — Verify no removed hooks are used
  6. Run testsbench --site test_site run-tests --app myapp

Decision Tree: In-Place vs Fresh Install

Choosing upgrade strategy:
├── Small site (< 10 GB database)?
│   └── In-place upgrade is usually fine
├── Large site (> 50 GB database)?
│   ├── Many custom apps? → Fresh install + data migration
│   └── Standard apps only? → In-place with extended downtime window
├── Skipping multiple versions (v13 → v15)?
│   └── ALWAYS fresh install — sequential upgrades are too risky
└── Critical production with zero-downtime requirement?
    └── Fresh install on parallel server + DNS switch

Version Differences Summary

Feature v14 v15 v16
Python packaging setup.py pyproject.toml pyproject.toml
Vue version Vue 2 Vue 3 Vue 3
Node.js minimum v14 v18 v24
Python minimum 3.8 3.10 3.14
Server Scripts Enabled Disabled default Disabled default
Default sort modified modified creation
patches.txt sections Yes Yes Yes
Workspace sidebar No No Yes
Separated modules Event Streaming Blog, Newsletter, EPS

Reference Files

File Contents
examples.md Complete upgrade workflow examples
anti-patterns.md Common upgrade mistakes and fixes
breaking-changes.md Detailed breaking changes per version
frappe-packages.md Packages, fixtures, and moving customizations between sites
Related skills
Installs
11
GitHub Stars
92
First Seen
Mar 24, 2026