Migrating from v26 to v27
This is the step-by-step walkthrough for upgrading a project from v26 to v27. For the authoritative catalogue of everything that changed, see v27 Breaking Changes →.
v27 migrates the entire electron-builder ecosystem to native ES modules, raises the minimum Node.js version to 22.12.0, and hard-deletes the deprecated APIs that accumulated since v22.
Every breaking change — what's removed, renamed, restructured, and what action each requires — is catalogued on the v27 Breaking Changes page. Skim it before you start so nothing surprises you.
Most projects need only a Node.js version bump plus one command. The build() API is unchanged and CJS require() continues to work without any code changes on supported Node.js versions. Configuration keys that were renamed or restructured (win.sign, mac.sign, electronGet, asar, nativeModules, fileOptions, …) are rewritten for you automatically by migrate-schema in Step 1 — they keep the same runtime behavior. A few type exports were renamed or removed; see the breaking changes.
The upgrade is three steps:
- Run the automated migrator — rewrites your config to v27 form
- Update Node.js — to >=22.12.0
- Apply the manual steps — the few things the migrator can't do
Then walk the summary checklist.
Step 1: Run the automated migrator
Run the built-in command to apply every config-level breaking change to your project automatically:
electron-builder migrate-schema # apply changes in place
electron-builder migrate-schema --dry-run # preview without writing (alias: -n)
This rewrites your electron-builder.json, electron-builder.yml, package.json (build key), or any other static config format in place. It also rewrites programmatic configs (.js/.ts/.cjs/.mjs) via an AST-located codemod that preserves comments, imports, functions, and formatting.
Without --config, it looks for a build object in package.json and then for electron-builder.{yml,yaml,json,json5,toml,js,cjs,mjs,ts} in the project root. If your config file has another name (for example one your build scripts pass as electron-builder --config my-config.cjs), pass the same path: electron-builder migrate-schema --config my-config.cjs. Pass --project-dir <dir> to specify the project root (default: current directory).
What it migrates automatically
| Config change | Before | After |
|---|---|---|
electronCompile removed | "electronCompile": true | (deleted) |
disableDefaultIgnoredFiles removed | "disableDefaultIgnoredFiles": true | (deleted) |
framework / nodeVersion / launchUiVersion removed | "framework": "electron" | (deleted) |
npmSkipBuildFromSource removed | "npmSkipBuildFromSource": true | "nativeModules": { "buildDependenciesFromSource": false } |
| Native-module options grouped | "npmRebuild": true | "nativeModules": { "npmRebuild": true } |
nativeRebuilder renamed | "nativeRebuilder": "parallel" | "nativeModules": { "rebuildMode": "parallel" } |
nativeRebuilder: "legacy" removed | "nativeRebuilder": "legacy" | (dropped with a warning — @electron/rebuild "sequential" applies) |
appImage.systemIntegration removed | "appImage": { "systemIntegration": "ask" } | (deleted) |
linux.syncDesktopName removed | "linux": { "syncDesktopName": true } | (deleted — always synced in v27) |
| Legacy asar keys | "asar-unpack": "**/*.node" | "asar": { "unpack": ["**/*.node"] } |
asarUnpack consolidated | "asarUnpack": ["**/*.node"] | "asar": { "unpack": ["**/*.node"] } |
disableSanityCheckAsar moved | "disableSanityCheckAsar": true | "asar": { "disableSanityCheck": true } |
disableAsarIntegrity moved | "disableAsarIntegrity": true | "asar": { "disableIntegrity": true } |
asar: true removed | "asar": true | (deleted — absence means enabled) |
Platform asarUnpack moved | "asarUnpack": ["**/*.node"], "mac": { "asarUnpack": ["x/**"] } | "asar": { "unpack": ["**/*.node"] }, "mac": { "asar": { "unpack": ["**/*.node", "x/**"] } } (root options merged in — a platform asar replaces the root one) |
Keys with no effect under asar: false | "asar": false, "asarUnpack": "x" | "asar": false |
macOS signing fields → sign | "mac": { "identity": "Developer ID...", "hardenedRuntime": true } | "mac": { "sign": { "identity": "Developer ID...", "hardenedRuntime": true } } |
signIgnore renamed | "mac": { "signIgnore": ["**/*.txt"] } | "mac": { "sign": { "ignore": ["**/*.txt"] } } |
mac.type moved | "mac": { "type": "development" } | "mac": { "sign": { "type": "development" } } |
gatekeeperAssess removed | "mac": { "gatekeeperAssess": false } | (deleted — @electron/osx-sign 2.x has no spctl --assess step) |
v26 null = unset on mac options | "mac": { "provisioningProfile": null } | (deleted — the v27 option rejects null; identity: null is kept) |
mac.universal fields → universal | "mac": { "mergeASARs": true } | "mac": { "universal": { "mergeASARs": true } } |
files / extraResources / extraFiles → fileOptions | "files": ["dist/**"], "mac": { "extraResources": ["r"] } | "fileOptions": { "files": ["dist/**"] }, "mac": { "fileOptions": { "extraResources": ["r"] } } |
Flat key next to the same fileOptions key | "files": ["a/**"], "fileOptions": { "files": ["b/**"] } | "fileOptions": { "files": ["b/**"] } (the fileOptions value takes precedence; warns) |
includePdb: true → fileOptions.excludedExts | "includePdb": true | "fileOptions": { "excludedExts": ["default", "!pdb"] } (the default extensions except pdb; "!pdb" is also appended to a root or platform fileOptions.excludedExts that excludes .pdb files; also for "true" and 1, which v26 read as true) |
includePdb: false → fileOptions.excludedExts | "includePdb": false | "fileOptions": { "excludedExts": ["default"] } (the v27 default, written out so that it keeps overriding an includePdb: true of another config layer, e.g. an extends parent; also for "false", 0 and null, which v26 read as false; next to an existing root fileOptions.excludedExts, includePdb is just removed) |
electronDownload → electronGet | "electronDownload": { "mirror": "https://m/" } | "electronGet": { "mirrorOptions": { "mirror": "https://m/" } } |
electronDownload fields with no v5 equivalent | "electronDownload": { "strictSSL": false, "force": true } | (dropped with a warning) |
GithubOptions.vPrefixedTagName | "vPrefixedTagName": false | "tagNamePrefix": "" |
GitHub empty tagNamePrefix (ignored by v26) | { "provider": "github", "tagNamePrefix": "" } | { "provider": "github", "tagNamePrefix": "v" } (keeps your tag names; warns) |
win.azureSignOptions → win.sign | "win": { "azureSignOptions": { "endpoint": "…" } } | "win": { "sign": { "type": "azure", "endpoint": "…" } } |
win.signtoolOptions → win.sign | "win": { "signtoolOptions": { "certificateFile": "…" } } | "win": { "sign": { "type": "signtool", "certificateFile": "…" } } |
| Azure extra sign keys | "win": { "azureSignOptions": { "ExcludeCredentials": "X" } } | "win": { "sign": { "type": "azure", "additionalMetadata": { "ExcludeCredentials": "X" } } } |
win.signExecutable: false | "win": { "signExecutable": false } | "win": { "sign": false } |
win.signExecutable: true removed | "win": { "signExecutable": true } | (deleted — enabled by default) |
win.signAndEditExecutable: true removed | "win": { "signAndEditExecutable": true } | (deleted — always enabled in v27) |
win.signAndEditExecutable: false | "win": { "signAndEditExecutable": false } | "win": { "sign": false } (resource editing now always runs; warns) |
| Signing options under disabled signing | "win": { "signExecutable": false, "signtoolOptions": { … } } | "win": { "sign": false } (the unused block is dropped; warns) |
snap → snapcraft | "snap": { "confinement": "strict", "base": "core22" } | "snapcraft": { "base": "core22", "core22": { "confinement": "strict" } } |
helper-bundle-id moved | "helper-bundle-id": "com.x.helper" | "mac": { "helperBundleId": "com.x.helper" } |
squirrelWindows.noMsi inverted | "squirrelWindows": { "noMsi": true } | "squirrelWindows": { "msi": false } |
Root-level directories moved | { "directories": { "output": "dist" } } (package.json root) | { "build": { "directories": { "output": "dist" } } } |
toolsets.*: null rejected | "toolsets": { "wine": null } | (deleted — unset resolves to "latest") |
Retired appimage pin | "toolsets": { "appimage": "1.0.2" } | "toolsets": { "appimage": "1.0.3" } |
It also warns without rewriting where there is no mechanical equivalent — squirrelWindows.customSquirrelVendorDir and a custom NSIS bundle in nsis.customNsisBinary / customNsisResources (see Step 3; it does move customNsisBinary.debugLogging → installerDebugLogging), a custom mac.sign signer next to sibling signing options, an includePdb value v26 rejected (e.g. "yes"), and, in JS/TS configs only, a platform-level asarUnpack next to root-level ASAR options, an includePdb whose value is not a literal, or, for includePdb: true, a platform section the codemod cannot read (e.g. win: sharedWin) — and prints advisories (never a rewrite) for an nsis-web target, nsis.perMachine or nsisWeb.perMachine set to true, a custom win.sign.sign hook without win.sign.publisherName, a generic publish configuration whose url has a query string, and a mac/mas/masDev section that names no entitlements file. See what migrate-schema does and does not do.
Each row links to its full explanation on the v27 Breaking Changes page.
Serialization caveats
- JSON5 → JSON: when
migrate-schemarewrites a.json5file it produces valid JSON (no comments, standard quoting) and prints a warning. If preserving comments matters, apply the changes manually. - TOML: the
tomlnpm package is read-only.migrate-schemadetects TOML configs, prints the required changes, and exits without writing. - YAML:
js-yamlpreserves key order when round-tripping but does not preserve comments. - Programmatic configs (
.js/.ts/.cjs/.mjs): rewritten in place when reducible to a single object literal; falls back to printing manual steps for dynamic function bodies, spreads, or computed keys, or whentypescriptis not installed. snapbase defaulting: when your oldsnapconfig has nobasefield, the tool assumes"core20"(v27's 1-to-1 migration target) and prints a warning so you can confirm or change it.
Step 2: Update Node.js
v27 requires Node.js 22.12.0 or later — the version where Node's require(esm) support was stabilized, allowing both CJS and ESM consumers to use these packages without code changes.
Update your local environment:
# nvm
nvm install 22 && nvm use 22
# fnm
fnm install 22 && fnm use 22
Update CI (GitHub Actions):
- uses: actions/setup-node@v4
with:
node-version: '22'
Update your package.json engines field if you declare one:
{ "engines": { "node": ">=22.12.0" } }
Confirm your CI and Docker images run Node.js 22.12 or later:
FROM node:22-bookworm-slim
electron-builder's own Docker images for Linux builds have been updated and are available in both node 22 and 24 flavors: https://hub.docker.com/r/electronuserland/builder/tags
node:22-bookworm-slim includes no flatpak tools. Install them with apt-get install -y flatpak flatpak-builder fuse3: Debian 12's flatpak 1.14.10 and flatpak-builder 1.2.3 meet the requirements. The electronuserland/builder images with a date tag newer than -05.26 include the tools, but no runtime, and the images up to -05.26 include neither — see Flatpak in the Docker Images. A container also needs FUSE and user namespaces, or the options for unprivileged containers — see Building Flatpak Bundles in CI and Containers.
ESM/CJS — no code changes needed on Node >=22.12
// CJS require() — still works
const { build } = require("electron-builder")
// ESM import — now the preferred style
import { build } from "electron-builder"
moduleResolution settings "node", "node16"/"nodenext", and "bundler" (recommended) all work. See Native ESM output for details.
Step 3: Apply the manual steps
migrate-schema handles every config-level change except squirrelWindows.customSquirrelVendorDir and a custom NSIS bundle in nsis.customNsisBinary / customNsisResources. The following require manual action — each links to its full explanation:
- Migrate off
electron-compileto a modern bundler (electron-vite / esbuild / webpack), if you usedelectronCompile. - Add
--publishexplicitly to your release scripts — auto-publish is gone. - Update-manifest signing: it is required on publishing builds — run
npx electron-builder create-update-keyonce, store the PEM base64-encoded on one line (base64 < update-private-key.pem | tr -d '\n') as a CI secret and expose it asELECTRON_BUILDER_UPDATE_SIGN_KEY, or setupdateManifest: falseto keep publishing unsigned manifests (warned about on every build). Every pipeline that can publish a release needs the key. - Replace
devMetadata/extraMetadatain the programmatic API withconfig.extraMetadata. - Replace
--em.build/--em.directoriesCLI flags with-c/-c.directories. -
linux.syncDesktopNameis removed automatically bymigrate-schema— the behaviour is now always on. Only if you had it set tofalse: setdesktopNameto control the installed.desktopfilename (the migrator warns you). - Switch
<%= var %>→${var}in Linux maintainer scripts. - Update custom NSIS scripts that hard-code the old file-association ProgID.
- Replace
CI_BUILD_TAGwithCI_COMMIT_TAG. - Replace toolset env-var overrides (
APPIMAGE_TOOLS_PATH,ELECTRON_BUILDER_NSIS_DIR,USE_SYSTEM_WINE, …) withtoolsets.X: { url, checksum }. - Validate toolset pins — defaults now resolve to
"latest"(newest bundle); pin to"0.0.0"only if a legacy bundle is needed. - 32-bit builds:
arch: "all"now expands to x64 + arm64 (was x64 + ia32) — requestia32explicitly to keep it. Building Windowsia32/ Linuxarmv7lon Electron 44+ now fails fast; pinelectronVersionto43.xor earlier (supported until January 2027) or drop those targets. - macOS names:
productName/executableNameare now validated, not silently sanitized — if a build starts erroring on the name, choose one that needs no filename sanitization. - Bitbucket publishing: a token set without a username is now sent as Bearer auth — if
BITBUCKET_TOKENholds an app password / API token, also setBITBUCKET_USERNAMEso Basic auth is used; genuine access tokens need no username. - S3 publishing: an explicit
awsCredentials.sourceis now required — addawsCredentials: { source: "env" }to keep usingAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, or{ source: "profile", profile: "<name>" }if you relied onAWS_PROFILE,~/.aws/credentials,~/.aws/config+AWS_SDK_LOAD_CONFIG=1or an SSO login. For a role assumed from an SSO profile viasource_profile, use{ source: "sso-role-chain", profile: "<role profile>" }. Setregionexplicitly for dotted bucket names. - node_modules arch/os filtering: dependencies are now
cpu/os-filtered against the target on every build — a package whosecpu/osmismatches the target is excluded (was effectively universal-macOS-only). Re-include an intentionally cross-arch binary viafileOptions.extraResources/fileOptions.files. -
.pdbfiles named infiles: afileOptions.filespattern that names.pdbfiles now includes the ones it matches (e.g.native/*.pdb,**/*.{node,pdb}), also in a configuration that extends this one — v26 excluded them unlessincludePdbwastrue.migrate-schemareports such patterns; to keep the.pdbfiles out, remove the pattern or add a negation such as!native/*.pdbafter it. - macOS DMG: the DMG
filesystemdefault is now APFS — setdmg.filesystem: "HFS+"only if you need pre-10.13 (High Sierra) compatibility. - electron-updater:
disableWebInstallernow defaults totrue— web-installer updates are rejected (ERR_UPDATER_WEB_INSTALLER_DISABLED) unless it isfalse. Installs made by a v27+nsis-webinstaller opt in automatically; setdisableWebInstaller: falseonly for web-installer installs without theresources/package-typemarker (pre-v27 installers, custom scripts) or when switching fromnsistonsis-web. Thensis-webinstaller now verifies and installs its own copy of the app package and leaves a local package file in place; setnsisWeb.allowUnverifiedAppPackage: trueonly if you use one web installer with packages of other builds. - NSIS per-machine updates: per-machine builds set
isAdminRightsRequiredin the update info — their updates are started withelevate.exe; withautoInstallEvent: "onNextLaunch", per-machine apps callinstallPendingUpdateIfAvailable()to install a pending update. - electron-updater: update credentials stay on the feed's origin — if
latest*.ymlpoints downloads at another origin that needs yourrequestHeaders/addAuthHeadercredentials or a token in the feed URL's query, serve the files from the feed origin or use pre-signed URLs. A custom provider used with credential headers must overrideProvider.feedBaseUrl(its feed URL, ornull), else downloads fail withERR_UPDATER_FEED_BASE_URL_NOT_DECLARED. - electron-updater:
latest*.ymlno longer includes legacy top-levelpath/sha512— no action for updater >=2.16; migrate any app code readinginfo.path/info.sha512toinfo.files[0], and setelectronUpdaterCompatibilityto a legacy-inclusive range only if you still ship 1.x–2.15 updaters. - Production dependencies: redundant production
dependenciesare excluded, not rejected —electron/electron-builderare excluded from the copiednode_modules(was a hard error) and theALLOW_ELECTRON_BUILDER_AS_PRODUCTION_DEPENDENCYenv var is removed (if you used it to bundleelectron-builder, overrideignoredProductionDependencieswithout that name); tune the set viaignoredProductionDependencies(e.g. add bundler-inlined deps likereact).electron-prebuilt/electron-rebuildno longer error and are NOT excluded — remove them fromdependencies(they are deprecated) or add them to the list. - Remove
squirrelWindows.customSquirrelVendorDir— the option is deleted; supply a custom Squirrel bundle viatoolsets.squirrel(aToolsetCustomobject) instead (note the different bundle layout). - Migrate any custom NSIS-binary and/or custom NSIS-resources to a
toolsets.nsisbundle —Potentially relevant for builds reliant on NSIS (nsisWeb,portable,nsis). The.customNsisBinaryand.customNsisResourcesconfig properties have been removed in favor of a new NSIS-bundle configured viatoolsets.nsis. The new toolset consolidates the removed properties and provides both the NSIS binary and resources. Configuring the removed properties fails the build.customNsisBinary.debugLoggingis nownsis.installerDebugLogging(moved bymigrate-schema); it needs a log-enabled NSIS (NSIS_CONFIG_LOG=yes), which the default toolset is not, so it requires a customtoolsets.nsisbundle. - Wine on macOS:
toolsets.winenow defaults to the host install — a macOS host that builds Windows targets needs Wine installed (brew install --cask wine-stable). - macOS entitlements: the bundled default now grants only
allow-jit— if your app loads native modules, frameworks, or plugins signed by another Team ID (or unsigned), adddisable-library-validation(andallow-unsigned-executable-memoryif needed) back inbuild/entitlements.mac.plist. - Missing dependencies: a production dependency that cannot be resolved now fails the build — fix the install or set
allowMissingDependencies. - Native binaries: a
.nodeaddon built for another platform/arch now fails the build — rebuild it for the target, or setnativeModules.verifyNativeBinariesto"warn"/false. - macOS ASAR integrity digest: with Electron 41+, the Electron Framework is patched and signed again ad hoc after
afterPack— setmac.asarIntegrityDigest: falseif you sign the app yourself inafterPack, changeapp.asar/Info.plistafter it, or merge separately built x64 and arm64 apps with@electron/universalyourself. -
extraFiles/extraResources: atothat is absolute or escapes the output dir now throws. - Windows
publisherName: is validated against the signing certificate — updatewin.sign.publisherNameafter a certificate rotation. A customwin.sign.signhook (even with a certificate configured), or a certificate without a CN, now needswin.sign.publisherNamefor builds that writeapp-update.yml(nsis/nsis-web/electronUpdaterAwareappxwith a publish config, including one inferred from a GitHubrepository); setwin.verifyUpdateCodeSignature: falseinstead only if your updates are not Authenticode-signed or you don't use electron-updater. - NSIS scripts: rename a
customInstallmodemacro to the documentedcustomInstallMode. - Linux launcher:
.desktopExecnow runs a*-launcherscript andexecutableArgsfield codes are literal — update custom.desktop/ AppArmor / MIME tooling and move%F/%Utolinux.desktop.entry.Exec. The flatpak target keeps itselectron-wrapperentrypoint. - Flatpak:
flatpak1.12.4+ andflatpak-builder1.2.2+ are required — the stock packages of Ubuntu 22.04, Debian 12 and newer meet this; update older tools on machines and images that build the flatpak target (theelectronuserland/builderimages with a date tag newer than-05.26include them, those up to-05.26none), and replaceDEBUG=@malept/flatpak-bundlerwithDEBUG=electron-builder. To build in an unprivileged CI container, see Building in unprivileged containers. - Flatpak runtime:
flatpak.runtimeVersionandflatpak.baseVersionnow default to26.08(was20.08, which is end-of-life) — a build that sets neither is built for the26.08runtime, and the build machine installs the26.08runtime, SDK and base app if they are missing (about 3.2 GB). Install them in images that held the20.08ones, or set both options to"20.08"to keep the previous runtime. - Flatpak permissions: the default
flatpak.finishArgsno longer include--filesystem=home— only for builds withoutfinishArgs: the app can reach the Downloads folder, its own data under~/.var/appand the files the user picks in a dialog. If it opens other paths on its own (recent files,~/Documents, dotfiles,~/.pki/nssdbcertificates), setfinishArgsto the v26 list or add narrower grants such as--filesystem=xdg-documents. - Flatpak notifications: the default
flatpak.finishArgsleave out--talk-name=org.freedesktop.Notificationson base app23.08and later, and with any otherbase— only for builds withoutfinishArgs: notifications go through the notification portal, so they no longer appear on desktops without a portal backend for notifications (for example sway with onlyxdg-desktop-portal-wlr). If your users run such desktops, add the talk-name tofinishArgs; a user can also runflatpak override --user --talk-name=org.freedesktop.Notifications <id>. - Update channels: suffixed channels (e.g.
beta-arm64) now expand to lower channels withgenerateUpdatesFilesForAllChannels— set it tofalseto keep one file per suffixed channel. - electron-updater API:
quitAndInstalltakes an options object,autoInstallOnAppQuit→autoInstallEvent, anddownloadUpdate()resolves with{ updateFile, packageFile }— update app code. - TypeScript imports: renamed type exports (
ElectronDownloadOptions→ElectronGetOptions, …) have no compat aliases. - Plugin/custom-target authors: replace
packager.info.XandplatformSpecificBuildOptionswith the new pass-through getters.
Step 4: Custom NSIS Migration Guide
If you do do not use either .customNsisBinary or .customNsisResources configurations on nsis nsis-web or portable in your <=v26 config there are no actions required and you can skip this step.
To migrate from .customNsisBinary/.customNsisResources to a new custom unified toolsets.nsis bundle requires manual steps.
A brief overview of this breaking change is good context before you start this migration. The NSIS Toolset Bundle docs may also be useful.
If you previously used .customNsisBinary in v26 or prior:
- Download & extract a copy of the new default NSIS Toolset Bundle from electron-builder-binaries as well as the archive you provided via
.customNsisBinary. - Depending on what OS(es) your
.customNsisBinarywas built to support, you may have any subset of these three binaries that need to be copied over.- a.
/Bin/makensis.exe - b.
/linux/makensis - c.
/mac/makensis
- a.
- The NSIS Toolset Bundle you download is a valid
toolsets.nsisbundle and provides easy locations you can drop anymakensisbinaries you into.- a.
/windows/← Copy/Bin/makensis.exeFrom your.customNsisBinaryarchive to this path to migrate - b.
/mac/x64← Copy/mac/makensisfrom a.customNsisBinaryarchive to this path to migrate - c.
/mac/arm64 - d.
/linux/x64← Copy/linux/makensisfrom a.customNsisBinaryarchive to this path to migrate - e.
/linux/arm64
- a.
- When copying your custom binaries across you should overwrite the makensis executables already present in the new-bundle.
- (optional) - As you can see, the new
toolset.nsisbundle now provides native makensis binaries for newly supported targets likeMac (arm64)&Linux (arm64). You're welcome to provide binaries for as many (or as few) Operating-System/Architecture targets as you want in your custom toolset. You only need to provide amakensis/makensis.exeexecutable for the OS & arch combos you intend to run your builds with. - (optional) - The new
toolsets.nsisis more flexible than previous.customNsisBinary/.customNsisResourcesin that the layout is customizable. The following files hook incoming makensis executions. This shim-script-layer opens up a path for custom dynamic logic to adjust binary/resources conditionally and on the fly.- a.
/makensis← Bash script, routes incoming makensis execution requests for macOS/Linux builds - b.
/makensis.cmd← Cmd script, routes incoming makensis execution requests for Windows builds running via Command Prompt - c.
/makensis.ps1← PowerShell script, may be used in the future by electron-builder to route incoming windows builds
- a.
- Include your newly created bundle in your electron builder config via
toolsets.nsisby either:- a. Providing a
file://<path-to-your-uncompressed-nsis-toolset-bundle-folder>as thetoolsets.nsis.url. - b. Similar to how
.customNsisBinary/.customNsisResourcesworked - host the bundle as a.7zfile remotely. Provide the remote url as thetoolsets.nsis.url& and provide the hash of the archive (either sha256-hex or sha512-base64) to thetoolsets.nsis.checksumproperty.
- a. Providing a
If you previously used .customNsisResources in v26 or prior:
-
Download & extract a copy of the new default NSIS Toolset Bundle from electron-builder-binaries as well as the archive you provided via
.customNsisResources. -
For the
.customNsisResources, all 3rd party resource .dlls were located as such:- a.
/plugins/x86-ansi/<plugin(s).dll> - b.
/plugins/x86-ansi/<plugin(s).dll> - c.
plugins/<Target>/<plugin(s)>.dll
- a.
-
The NSIS Toolset Bundle you download is a valid
toolsets.nsisbundle and provides an easy locations you can drop in your resources. The root NSISDIR (NSIS Resources Directory) is now/windows/. The move of just resources is pretty straightforward:- a.
/windows/Plugins/x86-ansi/<plugin(s).dll> - b.
/windows/Plugins/x86-ansi/<plugin(s).dll> - c.
plugins/<Target>/<plugin(s)>.dll
- a.
-
When copying your custom resources across you should overwrite the makensis executables already present in the new-bundle.
-
The complete common NSIS resources pulled by
makensisbinary runtime are/Stubs,/Include,/Contrib,/Plugins. Nothing outside of/pluginswas previously supported by.customNsisResources. With the new NSIS toolset, if you have any of these resources then they can go here:- a.
/windows/Plugins/ - b.
/windows/Include/ - c.
/windows/Contrib/ - d.
/windows/Stubs/ - e.
/windows/← Root NSISDIR (Nsis Resources Dir) by default
- a.
-
(optional) - The new
toolsets.nsisis more flexible than previous.customNsisBinary/.customNsisResourcesin that the layout is customizable. The following files configure NSISDIR (NSIS Resource Directory). This shim-script-layer opens up a path to dynamically control NSISDIR on the fly.- a.
/makensis← Bash script, routes incoming makensis execution requests for macOS/Linux builds - b.
/makensis.cmd← Cmd script, routes incoming makensis execution requests for Windows builds running via Command Prompt - c.
/makensis.ps1← PowerShell script, routes incoming makensis execution requests for Windows builds running via Powershell
- a.
-
Include your newly created bundle in your electron builder config via
toolsets.nsisby either:- a. Providing a
file://<path-to-your-uncompressed-nsis-toolset-bundle-folder>as thetoolsets.nsis.url. - b. Similar to how
.customNsisBinary/.customNsisResourcesworked - host the bundle as a.7zfile remotely. Provide the remote url as thetoolsets.nsis.url& and provide the hash of the archive (either sha256-hex or sha512-base64) to thetoolsets.nsis.checksumproperty.
- a. Providing a
-
The
toolsets.nsisbundle must carryelevate.exeat the bundle's root directory You can grab a copy one from the root of Electron Builder's default NSIS Toolset Bundle provided in electron-builder-binaries. It is used for privledge-control features by the UAC nsis plugin. -
For more information/guidance you can checkout the
toolsets.nsisdocumentation
Summary checklist
Run electron-builder migrate-schema first — it handles the items marked ✓ automatically.
Runtime and CI
- Node.js runtime updated to >=22.12.0
- CI
node-versionupdated to'22' -
"engines"field inpackage.jsonupdated (if declared)
Build config — auto-migrated by migrate-schema
- ✓
electronCompileremoved (if present) - ✓
disableDefaultIgnoredFilesremoved (if present; re-include specific files via afileOptions.filesglob such as**/*.obj) - ✓
framework,nodeVersion,launchUiVersionremoved (if present) - ✓
npmSkipBuildFromSource→nativeModules.buildDependenciesFromSource(if present) - ✓
buildDependenciesFromSource,nodeGypRebuild,npmRebuildmoved intonativeModules;nativeRebuilder→rebuildMode - ✓ Legacy asar keys +
asarUnpack→asar.unpack(platform-level too);disableSanityCheckAsar→asar.disableSanityCheck;disableAsarIntegrity→asar.disableIntegrity;asar: trueremoved - ✓ macOS signing fields →
mac.sign.*;signIgnore→sign.ignore(alsomas/masDev) - ✓
mac.universalfields →mac.universal.* - ✓
files/extraResources/extraFiles→fileOptions.*(root andmac/mas/masDev/win/linux) - ✓
includePdb→fileOptions.excludedExts(if present;true→["default", "!pdb"],false→["default"], the v27 default written out so that it keeps overriding anincludePdb: trueof another config layer; replace anincludePdbpassed programmatically or with-cby hand, e.g.-c.includePdb=false→-c.fileOptions.excludedExts=default,-c.includePdb=true→-c.fileOptions.excludedExts=default -c.fileOptions.excludedExts=!pdb(in an interactive bash or zsh, single-quote the second flag:'-c.fileOptions.excludedExts=!pdb'); a list of removals only, such as["!pdb"], is rejected because it would exclude no extension at all: write["default", "!pdb"]) - ✓
electronDownload→electronGet(verify droppedcache/customDir/customFilename/strictSSL/platform/arch/version/force) - ✓
appImage.systemIntegrationremoved (if present) - ✓
linux.syncDesktopNameremoved (if present; always synced in v27 — setdesktopNameif you had itfalse) - ✓
GithubOptions.vPrefixedTagName→tagNamePrefix(if present) - ✓
win.azureSignOptions/win.signtoolOptions→win.sign;win.signExecutable/win.signAndEditExecutablehandled - ✓
snaprestructured tosnapcraft(verify the assumedbaseif none was set) - ✓
helper-bundle-id→mac.helperBundleId;squirrelWindows.noMsi→msi; root-leveldirectories→build.directories - ✓
toolsets.*: nullremoved;toolsets.appimage: "1.0.2"→"1.0.3";nativeRebuilder: "legacy"dropped
Runtime defaults & auto-update
- DMG
filesystemdefault is now APFS — setdmg.filesystem: "HFS+"only for pre-10.13 macOS compatibility - macOS default entitlements now grant only
allow-jit— addallow-unsigned-executable-memory/disable-library-validationback inbuild/entitlements.mac.plist(and.inherit.plistfor nested binaries) only if your app loads foreign-signed or unsigned binaries; MAS builds withoutbuild/entitlements.mas.plistnow get osx-sign's sandboxed default - electron-updater
disableWebInstallerdefaults totrue— web-installer updates are rejected unless it isfalse; v27+nsis-webinstalls opt in automatically, setdisableWebInstaller: falsefor older web-installer installs or when switching fromnsistonsis-web; thensis-webinstaller verifies its app package (nsisWeb.allowUnverifiedAppPackageopts out) - A code-signed Windows build that writes
app-update.ymlneeds a publisher name — setwin.sign.publisherNamefor a customwin.sign.signhook or a certificate without a CN, orwin.verifyUpdateCodeSignature: falseif your updates are not Authenticode-signed - NSIS per-machine builds set
isAdminRightsRequired— their updates are started withelevate.exeand not installed automatically at launch; withautoInstallEvent: "onNextLaunch", callinstallPendingUpdateIfAvailable() - Update credentials stay on the feed's origin —
requestHeaders/addAuthHeadercredentials and the feed URL's query are not sent to downloads on another origin -
latest*.ymldrops legacy top-levelpath/sha512; defaultelectronUpdaterCompatibilityis now>=2.16— readfiles[]instead; pin a legacy-inclusive range only for legacy embedded updaters - Production
dependenciesthat electron-builder provides or that your bundler inlines (electron/electron-builder/react/…) are excluded from the copiednode_modulesinstead of erroring;ALLOW_ELECTRON_BUILDER_AS_PRODUCTION_DEPENDENCYremoved andelectron-prebuilt/electron-rebuildno longer guarded — tune viaignoredProductionDependencies - Building Windows targets on macOS uses the host Wine (
toolsets.winedefaults to"system") — install Wine on macOS build machines - Flatpak
runtimeVersion/baseVersiondefault to26.08(was20.08) — set both to"20.08"only to keep the previous runtime - Flatpak default
finishArgsgrant--filesystem=xdg-downloadinstead of--filesystem=home— setflatpak.finishArgsif the app needs home access - Flatpak default
finishArgsleave out--talk-name=org.freedesktop.Notificationson base app23.08and later, and with any otherbase— add it toflatpak.finishArgsfor desktops without a notification portal backend
Manual steps — see Step 3 & Step 4 above.
Plugin and custom-target authors — see the programmatic API changes.
Full breaking-changes reference
Every change above is documented in detail — with rationale (design notes) and before/after for each — on the v27 Breaking Changes page.