electron-builder
electron-builder
Docs: https://www.electron.build (v26.8.x) Repo: https://github.com/electron-userland/electron-builder
Quick Start
Install:
pnpm add electron-builder -D
pnpm add electron-updater # If using auto-updates
Minimal config (electron-builder.yml):
appId: com.example.myapp
productName: My App
files:
- "out/**/*"
- "package.json"
mac:
target: dmg
category: public.app-category.developer-tools
win:
target: nsis
linux:
target:
- AppImage
- deb
publish:
provider: github
Build scripts in package.json:
{
"scripts": {
"build:mac": "electron-builder --mac",
"build:win": "electron-builder --win",
"build:linux": "electron-builder --linux",
"build:all": "electron-builder -mwl",
"release": "electron-builder --publish always"
}
}
CLI Reference
electron-builder # Build for current platform
electron-builder -mwl # Build for all platforms
electron-builder --mac dmg # macOS DMG only
electron-builder --win nsis:ia32 # Windows NSIS 32-bit
electron-builder --linux deb tar.xz
electron-builder --dir # Unpacked dir (test builds)
electron-builder -p always # Build and publish
# Architecture flags
--x64 --ia32 --armv7l --arm64 --universal
# CLI config overrides
-c.extraMetadata.foo=bar
-c.mac.identity=null
-c.nsis.unicode=false
# Publish existing artifacts
electron-builder publish -f dist/*.exe -c electron-builder.yml
Publish flag values: onTag | onTagOrDraft | always | never
Configuration
Config locations (checked in order):
package.json>"build"keyelectron-builder.yml(default, recommended)electron-builder.json/.json5/.tomlelectron-builder.config.js/.ts- CLI:
--config <path>
Do NOT name JS config electron-builder.js — conflicts with package name.
For full configuration options, file patterns, macros, icons, and directory settings: See references/configuration.md
Essential Config Properties
| Property | Default | Description |
|---|---|---|
appId |
com.electron.${name} |
Do not change once deployed. Used as bundle ID (macOS) and AUMID (Windows). |
productName |
package.json name | Display name (allows spaces) |
compression |
"normal" |
"store" for fast test builds, "maximum" for release |
asar |
true |
Pack source into asar archive |
files |
auto | Glob patterns for app source files |
extraFiles |
— | Files copied outside asar (e.g. native addons) |
extraResources |
— | Files copied to resources directory |
forceCodeSigning |
false |
Fail build if not signed |
File Macros
Available in artifactName, file patterns, and publish URLs:
${arch}, ${os}, ${platform}, ${name}, ${productName}, ${version}, ${channel}, ${ext}, ${env.VAR_NAME}
Default Targets
| Platform | Default |
|---|---|
| macOS | DMG + ZIP |
| Windows | NSIS |
| Linux (cross) | Snap + AppImage (x64) |
| Linux (native) | Snap + AppImage (current arch) |
Code Signing
Signing is automatic when configured. Core environment variables:
| Env | Description |
|---|---|
CSC_LINK |
Certificate path/URL/base64 (.p12/.pfx) |
CSC_KEY_PASSWORD |
Certificate password |
CSC_IDENTITY_AUTO_DISCOVERY |
true/false (macOS keychain auto-discovery) |
WIN_CSC_LINK |
Windows cert (when cross-signing from macOS) |
WIN_CSC_KEY_PASSWORD |
Windows cert password |
macOS: Disable signing
export CSC_IDENTITY_AUTO_DISCOVERY=false
# Or in config: mac.identity: null
# For ad-hoc (ARM): mac.identity: "-"
macOS: Notarization
mac:
hardenedRuntime: true
notarize: true # or { teamId: "TEAM_ID" }
Requires APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID env vars.
Windows: Azure Trusted Signing
win:
azureSignOptions:
publisherName: "CN=Your Company"
endpoint: "https://eus.codesigning.azure.net"
certificateProfileName: "your-profile"
codeSigningAccountName: "your-account"
Requires AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET.
For complete code signing reference (CI setup, certificates, EV certs, cross-platform): See references/code-signing.md
Auto Update (electron-updater)
Minimal setup
// main process
import electronUpdater, { type AppUpdater } from "electron-updater";
export function getAutoUpdater(): AppUpdater {
const { autoUpdater } = electronUpdater;
return autoUpdater;
}
const autoUpdater = getAutoUpdater();
autoUpdater.checkForUpdatesAndNotify();
Do NOT call setFeedURL() — app-update.yml is auto-generated at build time.
ESM Import (required workaround)
// CORRECT
import electronUpdater from "electron-updater";
const { autoUpdater } = electronUpdater;
// WRONG (may fail with ESM)
import { autoUpdater } from "electron-updater";
Auto-updatable targets
- macOS: DMG
- Windows: NSIS
- Linux: AppImage, DEB, Pacman (beta), RPM
macOS apps MUST be signed for auto-update. Squirrel.Windows NOT supported.
Events
autoUpdater.on("error", (err) => {});
autoUpdater.on("checking-for-update", () => {});
autoUpdater.on("update-available", (info) => {});
autoUpdater.on("update-not-available", (info) => {});
autoUpdater.on("download-progress", (progress) => {
// .bytesPerSecond, .percent, .total, .transferred
});
autoUpdater.on("update-downloaded", (info) => {
autoUpdater.quitAndInstall();
});
Debugging
import log from "electron-log";
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = "info";
For staged rollouts, custom updater instances, private repos, dev testing: See references/auto-update.md
Publishing
Quick GitHub Releases setup
publish:
provider: github
releaseType: draft
Set GH_TOKEN env var (personal access token with repo scope).
Quick S3 setup
publish:
provider: s3
bucket: my-bucket-name
Set AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY.
Quick Generic Server setup
publish:
provider: generic
url: https://example.com/releases
Upload artifacts + latest.yml manually.
Publish CLI behavior
| Condition | Default behavior |
|---|---|
| CI detected | onTagOrDraft |
| CI + tag pushed | onTag |
npm script release |
always |
Release Channels
Version determines channel: 1.0.0 = latest, 1.0.0-beta.1 = beta
For all publishers (Bitbucket, GitLab, Keygen, Snap Store, Spaces), workflows, and advanced config: See references/publishing.md
Platform Target Configuration
macOS
mac:
category: public.app-category.developer-tools
hardenedRuntime: true
darkModeSupport: true
target: dmg
entitlements: build/entitlements.mac.plist
notarize: true
Windows (NSIS)
nsis:
oneClick: true # false for assisted installer
perMachine: false
allowToChangeInstallationDirectory: false
createDesktopShortcut: true
deleteAppDataOnUninstall: false
include: build/installer.nsh # Custom NSIS script
differentialPackage: true
Linux
linux:
category: Development
desktop:
MimeType: "x-scheme-handler/myapp"
target:
- AppImage
- deb
- snap
For all target options (DMG, PKG, MAS, MSI, AppX, Snap, Flatpak, portable, custom NSIS scripts): See references/platform-targets.md
Build Hooks
Execution order:
beforeBuild → beforePack → afterExtract → afterPack → [signing] →
afterSign → artifactBuildStarted → [build] → artifactBuildCompleted →
afterAllArtifactBuild
Inline (JS/TS config)
module.exports = {
afterSign: async (context) => {
if (context.electronPlatformName === "darwin") {
await notarize(context);
}
},
afterAllArtifactBuild: (result) => {
return ["/path/to/extra/file"]; // Additional files to publish
},
};
File reference (YAML config)
beforePack: "./scripts/before-pack.js"
afterSign: "./scripts/notarize.js"
// scripts/notarize.js
exports.default = async function(context) {
// context: { outDir, appOutDir, packager, electronPlatformName, arch, targets }
};
For all hooks, context interfaces, and programmatic API: See references/hooks-and-programmatic.md
Common Patterns
Multi-platform CI build
# GitHub Actions pattern
mac:
target:
- target: dmg
arch: [x64, arm64]
win:
target:
- target: nsis
arch: [x64, ia32]
linux:
target:
- target: AppImage
arch: [x64, arm64]
- target: deb
arch: [x64, arm64]
publish:
provider: github
Complete config with auto-update and signing
appId: com.example.myapp
productName: My App
copyright: Copyright 2024 Example Inc.
asar: true
compression: normal
forceCodeSigning: true
directories:
output: dist
buildResources: build
files:
- "out/**/*"
- "package.json"
mac:
target: [dmg, zip]
hardenedRuntime: true
notarize: true
category: public.app-category.developer-tools
win:
target: nsis
nsis:
oneClick: false
perMachine: false
allowToChangeInstallationDirectory: true
linux:
target: [AppImage, deb]
category: Development
publish:
provider: github
electronUpdaterCompatibility: ">= 2.16"
Gotchas
- Never change
appIdafter release — NSIS uses it for registry GUID - macOS signing required for auto-update to work
- ESM import workaround needed for electron-updater in TypeScript
- Don't call
setFeedURL()—app-update.ymlis auto-generated - Squirrel.Windows not supported by electron-updater — use NSIS
- Windows env var limit 8192 chars — re-export cert without chain if too large
electron-builder.jsconflicts with package name — use different filename- Set
app.setAppUserModelId(appId)in main process for Windows notifications