Electron Auto-Update Patterns
SkillAI & modelsCross-platform auto-update patterns with electron-updater (electron-builder ecosystem)
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Electron Auto-Update Patterns skill
What this skill tells your AI
The instructions your AI receives, as published by agents-inc/skills in src/skills/desktop-updates-electron-updater/SKILL.md and read by ahel’s review.
Quick Guide: Use
electron-updater(from electron-builder) for cross-platform auto-updates. It supports macOS (DMG), Windows (NSIS), and Linux (AppImage/DEB/RPM). Configure a provider (GitHub, S3, generic server) in yourelectron-builderconfig. The updater emits lifecycle events:checking-for-update->update-available->download-progress->update-downloaded. SetautoDownload: falsefor manual download control. Use channels (latest/beta/alpha) for staged releases andstagingPercentagefor gradual rollouts. Code signing is mandatory on macOS and strongly recommended on Windows.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST guard update checks with app.isPackaged -- calling checkForUpdates() in development causes confusing errors and network calls to non-existent endpoints)
(You MUST handle the error event on the updater -- unhandled update errors crash the main process)
(You MUST code-sign macOS builds -- unsigned apps cannot auto-update and the updater silently fails)
(You MUST NOT call quitAndInstall() without confirming the user's intent -- forcing a restart mid-work causes data loss)
(You MUST use named constants for all intervals and timeouts -- no magic numbers in setInterval or retry logic)
</critical_requirements>
Auto-detection: electron-updater, autoUpdater from electron-updater, checkForUpdates, checkForUpdatesAndNotify, update-available, update-downloaded, download-progress, quitAndInstall, autoDownload, stagingPercentage, dev-app-update.yml, NsisUpdater, MacUpdater, AppImageUpdater, setFeedURL, allowPrerelease, allowDowngrade, forceDevUpdateConfig, disableDifferentialDownload
When to use:
- Implementing auto-updates in Electron apps built with electron-builder
- Configuring update providers (GitHub Releases, S3, generic HTTP server)
- Setting up update channels for beta/alpha testing
- Implementing staged rollouts with percentage-based distribution
- Controlling download behavior (manual download, progress tracking)
- Handling update errors with retry strategies
- Testing the update flow locally during development
When NOT to use:
- Apps packaged with Electron Forge using Squirrel (use Electron's built-in
autoUpdatermodule instead) - Apps distributed exclusively through platform app stores (macOS App Store, Microsoft Store) -- those have their own update mechanisms
- Apps that only need to check for updates and show a "download from website" link (no in-app update needed)
Key Patterns
Pattern 1: Basic Setup with Lifecycle Events
Import autoUpdater from electron-updater (not Electron's built-in module). Wire up lifecycle events in the main process after the app is ready.
import { autoUpdater } from "electron-updater";
const CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
function setupAutoUpdater(mainWindow) {
if (!app.isPackaged) return; // Never check in development
autoUpdater.on("update-available", (info) => {
mainWindow.webContents.send("update-available", info);
});
autoUpdater.on("update-downloaded", (info) => {
mainWindow.webContents.send("update-downloaded", info);
});
autoUpdater.on("error", (error) => {
log.error("Update error:", error);
});
autoUpdater.checkForUpdatesAndNotify();
setInterval(() => autoUpdater.checkForUpdates(), CHECK_INTERVAL_MS);
}
Key point: checkForUpdatesAndNotify() checks and shows a native OS notification when an update downloads. Use checkForUpdates() for silent checks when you handle UI yourself. See examples/core.md.
Pattern 2: Manual Download Control
Set autoDownload: false to let users decide when to download. This is essential for metered connections or large updates.
autoUpdater.autoDownload = false;
autoUpdater.on("update-available", (info) => {
// Show UI prompt -- user decides whether to download
mainWindow.webContents.send("update-available", info);
});
// User clicks "Download" in the renderer
ipcMain.handle("start-update-download", () => {
return autoUpdater.downloadUpdate();
});
Key point: With autoDownload: false, the download-progress and update-downloaded events only fire after you explicitly call downloadUpdate(). See examples/core.md.
Pattern 3: Update Providers
Configure where the updater looks for releases. The provider is set in your electron-builder config file and can be overridden at runtime with setFeedURL().
# electron-builder.yml -- GitHub provider (default if GH_TOKEN set)
publish:
provider: github
owner: my-org
repo: my-app
# electron-builder.yml -- Generic HTTP server
publish:
provider: generic
url: https://releases.example.com/updates
# electron-builder.yml -- S3 bucket
publish:
provider: s3
bucket: my-app-releases
region: us-east-1
path: /releases
Key point: The first provider in the list is the auto-update source. Additional providers are publishing targets only. See examples/core.md for runtime setFeedURL() override.
Pattern 4: Update Channels (Stable/Beta/Alpha)
Channels distribute pre-release versions to specific user groups. Append -beta or -alpha to your package.json version to produce channel-specific metadata files.
{ "version": "2.1.0-beta" }
# electron-builder.yml
generateUpdatesFilesForAllChannels: true
// Switch channel at runtime
autoUpdater.channel = "beta";
// Setting channel automatically enables allowDowngrade
Key point: Users on alpha receive alpha, beta, and stable releases. Users on beta receive beta and stable. Users on latest (stable) only receive stable releases. See examples/channels-and-rollouts.md.
Pattern 5: Staged Rollouts
Roll out updates gradually by setting stagingPercentage in your metadata YAML file. The updater assigns each installation a persistent random ID and compares it against the percentage.
# latest.yml (manually edited after publishing)
version: 2.1.0
stagingPercentage: 10 # Ship to 10% of users first
Key point: Increment the version when pulling a broken staged release -- users already on the broken version will not downgrade to the same version number. See examples/channels-and-rollouts.md.
Pattern 6: Error Handling and Retry
Network failures during update checks are common. Wrap retry logic around the check and always handle the error event.
const MAX_RETRIES = 3;
const RETRY_DELAY_MS = 30_000; // 30 seconds
autoUpdater.on("error", (error) => {
log.error("Auto-update error:", error.message);
// Notify renderer for user-facing feedback
mainWindow.webContents.send("update-error", error.message);
});
Key point: The error event fires for network failures, signature verification failures, and corrupted downloads. Never ignore it -- unhandled errors in the updater crash the main process. See examples/core.md for retry with exponential backoff.
Pattern 7: Testing Locally
Use dev-app-update.yml and forceDevUpdateConfig to test the update flow without packaging.
# dev-app-update.yml (project root)
provider: generic
url: http://localhost:8080/updates
if (!app.isPackaged) {
autoUpdater.forceDevUpdateConfig = true;
}
Key point: You still need a local HTTP server serving the update artifacts (installer + latest.yml). Minio is commonly used as a local S3-compatible server for this purpose. See examples/testing.md.
<decision_framework>
Decision Framework
Which Update Approach?
Building with electron-builder?
+-- YES --> Use electron-updater (this skill)
+-- NO --> Building with Electron Forge?
+-- YES --> Using Squirrel maker?
| +-- YES --> Use Electron's built-in autoUpdater module
| +-- NO --> Can use electron-updater with custom config
+-- NO --> Distributing via app store?
+-- YES --> Use the store's native update mechanism
+-- NO --> Use electron-updater with generic provider
Which Provider?
Where are your releases hosted?
+-- GitHub Releases (public or private repo)
| +-- Use provider: github
+-- AWS S3 or compatible (MinIO, Backblaze B2)
| +-- Use provider: s3
+-- DigitalOcean Spaces
| +-- Use provider: spaces
+-- Any HTTP(S) server (Nginx, CDN, custom)
| +-- Use provider: generic
+-- Keygen (license-gated updates)
+-- Use provider: keygen
autoDownload: true vs false?
Should updates download automatically?
+-- App is small (<50 MB) and users expect seamless updates?
| +-- autoDownload: true (default) + checkForUpdatesAndNotify()
+-- App is large or users are on metered connections?
| +-- autoDownload: false + show download prompt in UI
+-- Enterprise environment with IT-managed rollouts?
+-- autoDownload: false + admin-controlled trigger
</decision_framework>
Detailed resources:
- examples/core.md - Setup, lifecycle events, manual download, providers, error handling with retry
- examples/channels-and-rollouts.md - Update channels, staged rollouts, channel switching
- examples/testing.md - Local testing, dev-app-update.yml, debugging with logging
- reference.md - API quick reference, event payloads, provider comparison, security checklist
<red_flags>
RED FLAGS
Critical Issues:
- Calling
checkForUpdates()orcheckForUpdatesAndNotify()outsideapp.isPackagedguard -- causes errors and unnecessary network calls in development - Not handling the
errorevent onautoUpdater-- unhandled update errors crash the main process - Shipping unsigned macOS builds -- auto-update silently fails without code signing
- Calling
quitAndInstall()immediately without user confirmation -- forces restart, risks data loss - Using Electron's built-in
autoUpdatermodule instead of importing fromelectron-updater-- different API, different behavior, no Linux support
Architecture Issues:
- Running update logic in the renderer process --
electron-updatermust run in the main process only - Checking for updates on every app launch without a cooldown -- hammers the update server, especially with large user bases
- Not using
autoInstallOnAppQuitwhenautoDownloadis true -- users never get the update if they don't explicitly restart - Mixing Squirrel.Windows and NSIS updater patterns -- they are incompatible (Squirrel uses
.nupkgdelta files, NSIS uses blockmap-based differential downloads)
Staged Rollout Mistakes:
- Setting
stagingPercentage: 0expecting it to block all updates -- behavior is undefined at 0; use channels for access control instead - Not incrementing version when pulling a broken staged release -- users already on the broken version stay there
- Editing
stagingPercentageinlatest.ymlwithout re-signing -- signature validation fails
Common Mistakes:
- Forgetting
generateUpdatesFilesForAllChannels: truewhen using beta/alpha channels -- only the current channel's YAML is generated - Using
allowPrerelease: trueon the client instead of proper channels --allowPrereleaseonly works with GitHub provider and is less predictable than channels - Not setting
autoUpdater.loggerduring debugging -- update failures are silent without logging configured - Hardcoding update URLs instead of using
electron-builderpublish config -- the build process auto-generates correct metadata only when publish is configured
Gotchas & Edge Cases:
checkForUpdatesAndNotify()returnsnullwhenapp.isPackagedis false -- it silently skips in dev- Differential downloads (blockmap) only work for NSIS on Windows -- macOS and Linux always do full downloads
quitAndInstall(true)(silent mode) only works on Windows NSIS -- macOS ignores theisSilentparameter- The
download-progressevent does not fire when differential download is used -- only fires for full downloads - On Windows, the updater verifies the code signature of the downloaded installer by default (
verifyUpdateCodeSignature) -- unsigned updates are rejected setFeedURL()overrides the provider fromelectron-builderconfig at runtime -- useful for switching environments but can cause confusion if called unintentionally
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST guard update checks with app.isPackaged -- calling checkForUpdates() in development causes confusing errors and network calls to non-existent endpoints)
(You MUST handle the error event on the updater -- unhandled update errors crash the main process)
(You MUST code-sign macOS builds -- unsigned apps cannot auto-update and the updater silently fails)
(You MUST NOT call quitAndInstall() without confirming the user's intent -- forcing a restart mid-work causes data loss)
(You MUST use named constants for all intervals and timeouts -- no magic numbers in setInterval or retry logic)
Failure to follow these rules will cause silent update failures, crashes, or data loss for end users.
</critical_reminders>
Signals
- GitHub stars
- 24
- Forks
- 8
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
desktop-updates-electron-updater- Source
- github.com/agents-inc/skills