Auto Update
Auto updates are enabled by the electron-updater package. Ideally, auto updates are configured to run in a CI pipeline to automatically provision new releases. See publish configuration for information on how to configure your local or CI environment for automated deployments.
Auto updates work as follows:
- You configure the package to build release metadata (
latest.yml) - Electron builder uploads the actual release targets and metadata files to the configured target (except for generic server, where you have to upload manually)
- You configure the Electron application to use auto-updates, which queries the publish server for possible new releases
Read the remainder of this guide to configure everything.
macOS application must be signed in order for auto updating to work.
Auto-updatable Targets
- macOS: DMG.
- Linux: AppImage, DEB, Pacman and RPM.
- Windows: NSIS.
All these targets are default, custom configuration is not required. (Though it is possible to pass in additional configuration, e.g. request headers.)
- Squirrel.Windows is not supported. Simplified auto-update is supported on Windows if you use the default NSIS target, but is not supported for Squirrel.Windows. You can easily migrate to NSIS.
ziptarget for macOS is required for Squirrel.Mac, otherwiselatest-mac.ymlcannot be created, which causesautoUpdatererror. Default target for macOS isdmg+zip, so there is no need to explicitly specify target.
Differences between electron-updater and built-in autoUpdater
The electron-updater package offers a different functionality compared to Electron's built-in auto-updater. Here are the differences:
- Linux is supported (not only macOS and Windows).
- Code signature validation not only on macOS, but also on Windows.
- All required metadata files and artifacts are produced and published automatically.
- Download progress and staged rollouts supported on all platforms.
- Different providers supported out of the box: (GitHub Releases, Amazon S3, DigitalOcean Spaces, Cloudflare R2, Keygen and generic HTTP(s) server).
- You need only 2 lines of code to make it work.
Quick Setup Guide
-
Install electron-updater as an app dependency.
-
Configure the
publishoptions depending on where you want to host your release files. -
Build your application and check that the build directory contains the metadata
.ymlfiles next to the built application. For most publish targets, the building step will also upload the files, except for the generic server option, where you have to upload your built releases and metadata manually. -
Use
autoUpdaterfromelectron-updaterinstead ofelectron:CommonJS
const { autoUpdater } = require("electron-updater")ESM
import { autoUpdater } from "electron-updater"TypeScript
import electronUpdater, { type AppUpdater } from 'electron-updater';export function getAutoUpdater(): AppUpdater {// Using destructuring to access autoUpdater due to the CommonJS module of 'electron-updater'.// It is a workaround for ESM compatibility issues, see https://github.com/electron-userland/electron-builder/issues/7976.const { autoUpdater } = electronUpdater;return autoUpdater;} -
Call
autoUpdater.checkForUpdatesAndNotify(). Or, if you need custom behaviour, implementelectron-updaterevents, check examples below.
Do not call setFeedURL. electron-builder automatically creates app-update.yml file for you on build in the resources (this file is internal, you don't need to be aware of it).
Examples
import { autoUpdater } from "electron-updater"
export default class AppUpdater {
constructor() {
const log = require("electron-log")
log.transports.file.level = "debug"
autoUpdater.logger = log
autoUpdater.checkForUpdatesAndNotify()
}
}
- A complete example showing how to use.
- An encapsulated manual update via menu.
Custom Options instantiating updater Directly
If you want more control over the updater configuration (e.g. request header for authorization purposes), you can instantiate the updater directly.
import { NsisUpdater } from "electron-updater"
// Or MacUpdater, AppImageUpdater
export default class AppUpdater {
constructor() {
const options = {
requestHeaders: {
// Any request headers to include here
},
provider: 'generic',
url: 'https://example.com/auto-updates'
}
const autoUpdater = new NsisUpdater(options)
autoUpdater.addAuthHeader(`Bearer ${token}`)
autoUpdater.checkForUpdatesAndNotify()
}
}
Install on Next Launch (Windows/Linux)
When a downloaded update is automatically installed is controlled by autoUpdater.autoInstallEvent ("manual" | "onQuit" | "onNextLaunch", default "onQuit"). With the default "onQuit", the update is installed when the app quits: the updater spawns the installer as a detached process while the app is exiting. If the quit happens because the OS session is ending (shutdown, reboot or log off on Windows), the OS can kill that installer mid-install and leave the app in a broken, partially-uninstalled state (#7807).
electron-updater ≥ 7.0 (electron-builder v27) mitigates this in two ways:
-
Session-end guard (always on). When the OS signals that the session is ending, the on-quit install is skipped and a warning is logged. The downloaded update stays cached and is installed on the next regular quit. Detection is best-effort:
powerMonitorshutdownon macOS/Linux, and theBrowserWindowsession-endevent on Windows (windowless, e.g. tray-only, apps cannot be covered on Windows). -
Install on next launch (opt-in). Instead of installing while quitting, persist the downloaded update and install it at the start of the next launch, when no session teardown can interrupt it:
autoUpdater.autoInstallEvent = "onNextLaunch"With this set, any app quit records the downloaded update as pending instead of spawning the installer. On the next launch the updater fetches fresh update info from your update server, re-validates the cached installer against it (checksum, and code signature on Windows), verifies the pending version is still an installable change (newer than the running app — or a downgrade when
allowDowngradeis enabled), then runs the installer silently and restarts the app. If validation fails or the version is no longer an installable change, the pending state is cleared and the app starts normally.Set
autoInstallEvent = "manual"to disable automatic installation entirely (the downloaded update stays cached until you callquitAndInstall()yourself).A single quit can also be deferred without setting the property:
autoUpdater.quitAndInstall({ isSilent: true, isForceRunAfter: true, waitUntilNextLaunch: true })You can also trigger the pending install explicitly (e.g. before opening your first window):
await autoUpdater.installPendingUpdateIfAvailable()
Per-target behavior
The automatic install at startup only runs for targets that can install the pending update without an elevation/authentication prompt. Targets that always elevate to install (deb/rpm/pacman via pkexec/sudo, per-machine NSIS via UAC) skip the automatic path with a log message and keep the pending update — call installPendingUpdateIfAvailable() explicitly at a moment your app controls to install those.
| Target | Automatic install at next launch | Explicit installPendingUpdateIfAvailable() |
|---|---|---|
| NSIS (per-user) | ✓ | ✓ |
NSIS (per-machine, isAdminRightsRequired) | skipped — would show a UAC prompt at startup | ✓ (UAC prompt) |
| AppImage | ✓ | ✓ |
| deb / rpm / pacman | skipped — package managers always elevate (pkexec/sudo) | ✓ (auth prompt) |
| macOS | n/a — Squirrel.Mac stages updates natively and applies them on relaunch | resolves false |
autoInstallEvent defaults to "onQuit" in v27; "onNextLaunch" is planned to become the default in v28 to resolve this class of session-end corruption once and for all. macOS is unaffected: Squirrel.Mac natively stages downloaded updates and applies them on relaunch, without a killable installer process (there "onQuit" and "onNextLaunch" behave identically).
Deferring the install to the next launch moves the installer out of the session-end quit — the common #7807 trigger — but it does not make the install itself atomic. If the user launches the app while the OS is already shutting down or rebooting (for example, right before a scheduled restart fires), the automatic startup install spawns the same detached NSIS/AppImage installer, and the OS can still terminate it mid-write.
The session-end guard does not cover this launch window: it is wired into the on-quit path only, and on a fresh launch the shutdown / session-end signal generally arrives after the startup install has already been spawned, so it cannot reliably win the race. The probability is very low — it requires a deferred update, a launch inside the shutdown window, and the OS killing the installer mid-write — and macOS is immune (Squirrel.Mac swaps the app bundle atomically). The only complete fix for NSIS/AppImage is an atomic install, which is out of scope for this feature.
Debugging
You don't need to listen to all events to understand what's wrong. Just set logger.
electron-log is recommended (it is an additional dependency that you can install if needed).
autoUpdater.logger = require("electron-log")
autoUpdater.logger.transports.file.level = "info"
Note that in order to develop/test UI/UX of updating without packaging the application you need to have a file named dev-app-update.yml in the root of your project, which matches your publish setting from electron-builder config (but in yaml format).
In latest version you need force the updater to work in "dev" mode:
autoUpdater.forceDevUpdateConfig = true
If you see this in logs:
APPIMAGE env is not defined, current application is not an AppImage
you need to apply this workaround otherwise update won't continue:
process.env.APPIMAGE = path.join(__dirname, 'dist', `app_name-${app.getVersion()}.AppImage`)
But it is not recommended, better to test auto-update for installed application (especially on Windows). Minio is recommended as a local server for testing updates.
Compatibility
Since electron-builder 27 the generated latest*.yml targets the modern files[] metadata format by default and no longer includes the legacy top-level path/sha512 fields, which are needed only by electron-updater 1.x – 2.15.0.
The electronUpdaterCompatibility option sets the electron-updater compatibility semver range (e.g. >=2.16, >=1.0.0). Can be specified per platform (e.g. win.electronUpdaterCompatibility). When the declared range intersects legacy electron-updater versions, the corresponding legacy fields are emitted in addition to files[]: the top-level path/sha512 and the Windows sha2 checksum for ranges intersecting <2.16.0, and the old latest-mac.json for ranges intersecting <2.0.0. Defaults to >=2.16.
Metadata format history:
1.0.0latest-mac.json2.15.0path2.16.0files
Staged Rollouts
Staged rollouts allow you to distribute the latest version of your app to a subset of users that you can increase over time, similar to rollouts on platforms like Google Play.
Staged rollouts are controlled by manually editing your latest.yml / latest-mac.yml (channel update info file).
version: 1.1.0
files:
- url: TestApp Setup 1.1.0.exe
sha512: Dj51I0q8aPQ3ioaz9LMqGYujAYRbDNblAQbodDRXAMxmY6hsHqEl3F6SvhfJj5oPhcqdX1ldsgEvfMNXGUXBIw==
size: 62021782
stagingPercentage: 10
Update will be shipped to 10% of userbase.
If you want to pull a staged release because it hasn't gone well, you must increment the version number higher than your broken release. Because some of your users will be on the broken 1.0.1, releasing a new 1.0.1 would result in them staying on a broken version.
File Generated and Uploaded in Addition
latest.yml (or latest-mac.yml for macOS, or latest-linux.yml for Linux) will be generated and uploaded for all providers except bintray (because not required, bintray doesn't use latest.yml).
Private GitHub Update Repo
You can use a private repository for updates with electron-updater by setting the GH_TOKEN environment variable (on user machine) and private option.
If GH_TOKEN is set, electron-updater will use the GitHub API for updates allowing private repositories to work.
Private GitHub provider only for very special cases — not intended and not suitable for all users.
The GitHub API currently has a rate limit of 5000 requests per user per hour. An update check uses up to 3 requests per check.
Events
The autoUpdater object emits the following events:
Event: error
errorError
Emitted when there is an error while updating.
Event: checking-for-update
Emitted when checking if an update has started.
Event: update-available
infoUpdateInfo (for generic and github providers) Emitted when there is an available update. The update is downloaded automatically ifautoDownloadistrue.
Event: update-not-available
Emitted when there is no available update.
infoUpdateInfo (for generic and github providers)
Event: download-progress
progressProgressInfobytesPerSecondpercenttotaltransferred
Emitted on progress.
Event: update-downloaded
infoUpdateInfo — for generic and github providers. VersionInfo for Bintray provider.
Event: update-cancelled
infoUpdateInfo
Emitted when an update is cancelled by the user or the download is otherwise aborted.
Event: appimage-filename-updated
pathstring
Emitted after a successful AppImage update when the file is moved to its final location. Only fires on Linux AppImage builds.
UpdateInfo
Interface: UpdateInfo
Extended by
Properties
files
readonlyfiles:UpdateFileInfo[]
minimumSystemVersion?
readonlyoptionalminimumSystemVersion?:string
The minimum version of system required for the app to run. Sample value: macOS 23.1.0, Windows 10.0.22631.
Same with os.release() value, this is a kernel version.
path?
readonlyoptionalpath?:string
Legacy top-level download descriptor for electron-updater 1.x – 2.15.0. Modern clients read files.
Only emitted when electronUpdaterCompatibility includes legacy clients, so it may be absent.
Deprecated
releaseDate
releaseDate:
string
The release date.
releaseName?
optionalreleaseName?:string|null
The release name.
releaseNotes?
optionalreleaseNotes?:string|ReleaseNoteInfo[] |null
The release notes. List if updater.fullChangelog is set to true, string otherwise.
sha512?
readonlyoptionalsha512?:string
Legacy top-level checksum for electron-updater 1.x – 2.15.0. Modern clients read files[].sha512.
Only emitted when electronUpdaterCompatibility includes legacy clients, so it may be absent.
Deprecated
stagingPercentage?
readonlyoptionalstagingPercentage?:number
The staged rollout percentage, 0-100.
version
readonlyversion:string
The version.