Moving ERPNext customizations to version control with fixtures

Customizations made in the ERPNext UI live in the database, so they are hard to review, test and roll back. Fixtures and custom folders in an app move them into git, with overwrite behaviour to plan for.

On this page

Moving ERPNext customizations to version control with fixtures: when teams need it

Changes made through the ERPNext interface, such as custom fields, property setters, scripts and print formats, are stored in the database of the site where they were made. Nothing in that change is written to a file that git can track, so a staging site and a production site can quietly drift apart as different people edit each one. The first question is whether your team has reached the point where the database is the only record of how the system is configured. Teams usually reach that point when they run more than one site, when more than one developer changes the same forms, or when an auditor or internal control owner asks who changed a field and when.

The usual route out of this is to move the customizations into files. Frappe's documentation describes exporting customizations from a site so they can be stored with an app, and keeping them in a custom folder inside an app module so that they are loaded when the app is installed or migrated. The exported files are then committed to git like any other source code, which gives you a history, a review step and a way to rebuild a site from the same definitions. Before you commit to this, confirm in the current Frappe documentation which customization types are exported, how each one is named in the files, and whether any of them still need to be created by hand on a new site.

Treat the need as a decision for your team, not a default. A single site maintained by one administrator may not gain much from this, but a team running development, staging and production has to agree on which environment is the source of truth and who is allowed to change it. Plan for the overwrite behaviour before the first sync: when a migrate runs, exported definitions can replace what is currently in the database. Ask your partner or your own developers to walk through a test migration on a copy of production, and check the result in a git diff before any change reaches live users.

Which export route fits which customization

Export Customizations is the built-in route that writes selected customization records from a site into JSON files inside a custom app module. It suits teams that built most of their changes through the UI and want a fast snapshot of that work. The export captures what you select at the time, so it is a point-in-time copy rather than a live link to the database.

  • Custom fields and property setters: Usually captured cleanly, since they are stored as records that describe changes to standard DocTypes.
  • Print formats and reports: Exported when selected, but review the file for hard-coded company names, logos and addresses before committing.
  • Scripts and workspaces: Client and server scripts and workspace layouts can be exported, but a server script is only meaningful on a site where server scripts are enabled.
  • Maintenance: A person must rerun the export after every change and decide what to include, which makes this route easy to forget.
  • Overwrite risk: Re-exporting replaces files, so confirm the diff in git before each commit.

Before relying on this route, confirm with your own test site which record types your exact ERPNext version exports and how it handles deleted records.

Configuration options and when each fits

This table compares the four ways customizations leave the database and reach git: Export Customizations in the Desk, fixtures declared in hooks.py, JSON files in a module's custom folder, and sync on bench migrate. Read each row as a question: what gets captured, what gets missed, and what you pay for it later.

OptionWhat it captures and missesMain trade-off
Export Customizations (Desk)Selected Custom Fields, Property Setters and Client Scripts exported to JSON from the chosen module; records outside the selection are not includedFast to produce, but the export is a snapshot that must be repeated after every UI change
Fixtures in hooks.pyFiltered records of named DocTypes (for example, specific Custom Fields or Print Formats) that the app ships on install and migrateFilters control scope precisely, but a broad filter can overwrite values edited on live sites
Custom folder JSON in a moduleCustomization files kept in the app's custom directory and loaded when the module is migrated; the app repository holds the source of truthClean git history and review, but developers must edit files by hand or re-export carefully
Sync on bench migrateApplies JSON and fixture files to the target site during migration, updating existing records to match the filesMakes deployments repeatable, but local UI edits not reflected in files are overwritten
Records created by users (data)Transactional and master data such as invoices, items and customers, which these mechanisms do not coverKeep data out of customization files; move it through the Data Import tool or a migration plan
Server scripts created in the UIStored in the database only, unless exported, and disabled by default from version 15Hard to review in git; move logic into an app module when it must be versioned

How to set up the export and check it

This sequence moves ERPNext customizations from a development site into a git repository and proves that a second site rebuilds the same configuration from those files. Each step ends with a check so the team knows the export is complete before it relies on it.

  1. Create or pick the custom app and repository

    Choose the custom app that will hold the customizations, or create it with bench new-app, then initialize a git repository for it and push it to the hosting platform your team uses. The engineer leading the work confirms the app is installed on the development site and that its folder structure is committed as a clean baseline. Done means the repository holds an empty baseline commit and the app appears in the bench apps list on the development site.

  2. Enable export in the development site

    On the development site only, enable developer mode in the site configuration so that changes made through the desk are written to files, and confirm that the custom app is the target module for new customizations. Keep production and test sites out of this step, since developer mode is meant for building, not for serving users. Done means a test change, such as a custom field, is recorded as a file inside the app's module folder rather than existing only in the database.

  3. Define fixture filters

    Decide which records belong in version control and write them into the app's hooks file as fixtures, using filters that limit each DocType to the customizations your team owns, such as custom fields, property setters, custom scripts or specific print formats. Exclude transactional data and anything that differs by environment, such as company records, users or API keys. Done means the hooks file lists each fixture with its filter, and a reviewer can explain from the file alone what will be exported.

  4. Export the customizations

    Run bench export-fixtures for the custom app on the development site so that the filtered records are written as JSON files in the app's fixtures folder, and export any custom scripts or other module-level items the app holds. Run the export again after each change, so files do not drift behind the database. Done means the fixtures folder contains one file per fixture type and the file count matches what the filters should select.

  5. Review the diff before committing

    Run git diff and read every changed file, checking for unintended records, environment-specific values such as domain names or email addresses, and deleted items that should still exist. Compare the exported files against the database on the development site by spot-checking several records in the desk, confirming each field value matches its JSON entry. Done means the diff contains only the changes you intended, and each spot-checked record matches its exported file.

  6. Commit and push the customizations

    Commit the app folder with a message that names the customizations included, then push the branch and open a review so a second person checks the diff. Keep the commit limited to the custom app so that ERPNext core code is not modified. Done means the reviewed commit is on the shared branch and the repository history shows the customizations as discrete, traceable changes.

  7. Run migrate on a second site

    Pull the committed app onto a second site, install it with bench install-app if it is not present, and run bench migrate so that the fixtures are imported from the repository. Confirm that the second site has no developer-mode-only customizations that were not in the repository by checking the custom fields, scripts and print formats in the desk. Done means the second site shows the same customizations as the development site and the migrate log reports no import errors.

  8. Confirm overwrites and record the result

    Test one deliberate change on the second site to see whether migrate overwrites it, since fixtures can replace records that were edited directly in the database. Record which customizations were overwritten, which survived, and any records that were skipped, and share that list with the team before the next release. Done means the team has a written note of the overwrite behaviour that they can apply to future migrations.

What overwrites on migrate and how to plan for it

Fixture sync on migrate can overwrite records that someone edited directly in the target site. If a Custom Field, Property Setter or other exported record was changed in production, the next migrate that loads the file from git may replace that change with the version in the repository. Before the first deploy, list every place where these records can be edited, including the desk UI on staging and production. Then confirm in the Frappe documentation which DocTypes are exported and how each record is matched on import. Treat any direct edit made outside git as a change that must be carried into the repository, or it will be lost on the next deploy.

Exported files can also carry stale values when developers edit the same customization in more than one place, such as a local bench, a shared development site and a feature branch. Each export reflects the state of the site it came from, so two branches that touch the same field can produce conflicting JSON that git merges without any warning. Name one environment as the source of truth for each customization type, usually the version-controlled app on a developer machine. Make every other environment a consumer that receives changes only through migrate, and write that rule into the repository's contributing notes so the team knows where each change starts.

Review the diff before every deploy. Run git diff on the custom folder of the app module, read each changed JSON file, and check that every removed or altered entry was intended. Take a backup of the target site first, then run the migrate in a staging copy that mirrors production data and compare the resulting records with what the diff predicted. Ask your partner or internal lead to confirm the exact overwrite behaviour for your ERPNext and Frappe version against the official documentation, and record the outcome in the release notes so the same check runs on every release.

Security and operational risks of exporting customizations

Moving customizations into git puts configuration, scripts and metadata in places they did not previously live. The cards below cover the exposure, review and drift risks that come with that move, and the checks to build into the workflow before the first export is committed.

Exported files may contain secrets

Fixture exports serialize the records and settings they capture, so a Custom Field, Print Format, Webhook or Notification can carry values that should never reach a repository, such as tokens, endpoint URLs with credentials or personal data in sample text. Before committing, scan the exported JSON and HTML for credentials and personal data, and confirm the repository's access and retention rules match the data classification of the site.

Server scripts need explicit review

Server Scripts run Python on document events or as API endpoints. From version 15 they are disabled by default and must be enabled with the server_script_enabled setting, so a script that exports cleanly may not run on a target site until someone turns that setting on. Treat every exported script as code: review it in a merge request, confirm who is allowed to enable the setting, and decide whether the logic belongs in a custom app instead.

Repository access becomes production access

Anyone who can merge to the branch that deploys to production can change business logic, permissions-adjacent behaviour and document workflows on live data. Restrict write access to the customization repository, require review from a second person for changes to scripts and workflows, and keep the deployment pipeline's credentials separate from developer accounts.

Environments drift apart

If changes are made in the UI on production, or exports are taken from one site and not another, the repository stops describing what runs. Adopt a rule that changes are made in development, exported and merged, and never edited directly in production. Compare the exported state with the live site after each deployment so drift is found before it causes a failed migration.

Merge conflicts in JSON exports

Exported DocType and Custom Field files are JSON with ordering and generated values that change between exports, so two developers editing the same form produce noisy conflicts that are easy to resolve incorrectly. Export from one developer's site at a time, keep changes to a given DocType in one branch, and review the diff for unintended property changes such as altered labels, options or default values.

Backup before every migrate

Running bench migrate applies pending patches and syncs customizations from app folders, and the overwrite behaviour can replace database changes that were never exported. Take a database backup and confirm it restores before each migrate on a shared environment, and record which commit was deployed so the site can be returned to a known state if a sync changes records unexpectedly.

Questions teams ask before exporting ERPNext customizations

Who does the work of moving ERPNext customizations into git?

Your team can do most of it, and a partner usually takes on the parts that need design decisions. The work splits into inventorying every customization (custom fields, property setters, scripts, print formats, reports and workflows), deciding which ones belong in an app's custom folder and which should stay in the database, exporting them, and then proving that a migrate on a clean copy reproduces the same behavior. Someone on your side must own the decisions about what is in scope, because each one changes what gets versioned and reviewed. Effort rises with how many customizations were made directly in the UI over the years. Ask any partner for a written scope that lists the inventory, the export set and the test steps, then request a quote from that scope.

How long does it take to set up customization version control?

No duration can be given from the sources available for this page, so the honest answer depends on the inventory. The main drivers are the number of custom fields, scripts, print formats and reports, whether those were built in the UI or in an app, and how many sites share the same customizations. A small, single-site setup with a clean inventory is far quicker to bring under git than a multi-site estate with years of ad hoc changes. Ask for a timeline as part of the written scope, with milestones for the inventory, the export, and the clean-environment test.

What must our team provide before the work starts?

You need three things in place before any export begins. First, a git repository your organization controls, with branch rules and review requirements that match how you already ship code. Second, at least one non-production environment that mirrors production's ERPNext version and installed apps, so exports and migrations can be tested without touching live data. Third, a named owner who can approve scope decisions, review exported files and sign off on the test results. Access to the bench host, database backups and the team that maintains your apps should also be confirmed in writing.

Can we export ERPNext customizations ourselves?

Yes, many teams do the first export in-house, using the Export Customizations option and the bench commands available in their installed version. The risk is not the export itself but the overwrite behavior: fixtures and custom folders are synced on migrate, so a change made in one environment can silently replace a change made in another. Teams that do it alone should document a rule for where customizations are made, run a migrate on a clean copy, and check the diff in git before merging. If your team lacks Frappe experience, a partner can review that process or run the first cycle with your staff.

What drives the effort up or down?

Four factors matter most. The number of customizations is the biggest, since each field, script and print format needs review and a test. The number of sites matters next, because every site that shares an app must match the exported state, and drift between sites multiplies the work. Legacy UI changes are the third factor: customizations made directly in the database, outside any app, often need to be re-created or audited before they can be exported cleanly. The fourth is how much of the logic relies on server scripts, which depend on whether server scripts are enabled on your hosting. Ask a partner to price each of these as separate line items in the quote.

What does the work cost and what recurs afterward?

Costs fall into one-time and recurring groups. The one-time cost covers the inventory, the export, the clean-environment test and the team's training on the new workflow. The recurring cost covers reviewing changes that land in git, running migrate tests before upgrades, and keeping the repository and environments aligned. ERPNext itself is free to self-host under GPL-3.0, according to the ERPNext pricing page, so your spend is on people, infrastructure and any support plan you choose. Request a quote that separates the one-time export from any ongoing retainer for upgrades and maintenance.

Will customizations survive a later ERPNext upgrade?

They can, but only if they are versioned and tested against each target release. Frappe's supported versions page lists which major versions are currently supported, so check your installed version against it before you plan the export. Exported fixtures and app code still need to be re-tested on the new version, because a breaking change in a standard DocType can affect a custom field or script. Plan for a test migrate on a copy of production as part of every upgrade, and confirm with your partner whether upgrade testing is included in the written scope.

How DeployOpen scopes and delivers customization version control

DeployOpen treats moving ERPNext customizations into git as a fixed-scope piece of engineering work. We inventory what lives only in the database, decide what belongs in an app and what can stay as exported fixtures, and agree the result in writing before any export or refactor begins.

How we scope

  • Inventory custom fields, property setters, scripts, print formats, reports and workspaces that exist only on the target site.
  • Classify each item as fixture, app code, or site-specific data that should never enter the repository.
  • Agree in writing which sites, versions and export lists are covered, and what the client must test before sign-off.

How we build

  • Set up the repository layout, custom folders in the app module and the export list used for each customization type.
  • Export customizations from a clean staging copy, review the generated JSON diffs and remove environment-specific values.
  • Test a full rebuild on a fresh bench, confirming that migrate applies the exported customizations and overwrites nothing unintended.

How we hand over

  • Deliver the repository, export runbook and a short guide to the overwrite behaviour your team must plan for on each migrate.
  • Document the release procedure: which changes are exported, who reviews the diff and how production is updated.
  • Walk your developers through the workflow on a real change so they can repeat it without us.

How we support

  • Engagements are fixed-scope, and ongoing support, upgrades and continuous testing are available as a retainer.
  • Review new customizations before they merge, so fixtures do not drift from what the app expects after ERPNext upgrades.
  • Confirm with your team and your ERPNext version which upgrade notes apply before each minor or major update.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.