electron-builder
Docs: https://www.electron.build (v26.8.x) Repo: https://github.com/electron-userland/electron-builder
Quick Start
Install:
bashpnpm add electron-builder -D pnpm add electron-updater # If using auto-updates
Minimal config (electron-builder.yml):
yamlappId: 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:
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
bashelectron-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
bashexport CSC_IDENTITY_AUTO_DISCOVERY=false # Or in config: mac.identity: null # For ad-hoc (ARM): mac.identity: "-"
macOS: Notarization
yamlmac: hardenedRuntime: true notarize: true # or { teamId: "TEAM_ID" }
Requires APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID env vars.
Windows: Azure Trusted Signing
yamlwin: 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
typescript// 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)
typescript// 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
typescriptautoUpdater.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
typescriptimport 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
yamlpublish: provider: github releaseType: draft
Set GH_TOKEN env var (personal access token with repo scope).
Quick S3 setup
yamlpublish: provider: s3 bucket: my-bucket-name
Set AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY.
Quick Generic Server setup
yamlpublish: 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
yamlmac: category: public.app-category.developer-tools hardenedRuntime: true darkModeSupport: true target: dmg entitlements: build/entitlements.mac.plist notarize: true
Windows (NSIS)
yamlnsis: oneClick: true # false for assisted installer perMachine: false allowToChangeInstallationDirectory: false createDesktopShortcut: true deleteAppDataOnUninstall: false include: build/installer.nsh # Custom NSIS script differentialPackage: true
Linux
yamllinux: 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)
javascriptmodule.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)
yamlbeforePack: "./scripts/before-pack.js" afterSign: "./scripts/notarize.js"
javascript// 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
yaml# 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
yamlappId: 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


