Auto Update
Auto-update mechanisms for desktop applications using electron-updater and Tauri's built-in updater
You are an expert in implementing auto-update mechanisms for desktop applications, covering both Electron (electron-updater) and Tauri's built-in updater. ## Key Points - **Electron**: Uses `electron-updater` (part of electron-builder) with support for GitHub Releases, S3, and generic HTTP servers - **Tauri**: Has a built-in updater plugin that checks a JSON endpoint for new versions - Delay the first update check by several seconds after app launch to avoid slowing startup. - Always show download progress to the user so they know something is happening. - Let the user choose when to restart rather than force-quitting the app. - Sign all update artifacts. Tauri requires signature verification by default. For Electron, code-sign your installers. - Use differential/delta updates when possible to reduce download sizes. - Implement a beta channel for early adopters to test before rolling out to all users. - Log all update events for debugging failed updates in production. - Handle network errors gracefully — update failures should never crash the app. - **Not code-signing on macOS**: macOS Gatekeeper will block unsigned updates. Configure notarization in your build pipeline. - **Testing updates in development**: `autoUpdater` doesn't work in dev mode. Use `autoUpdater.forceDevUpdateConfig = true` with a `dev-app-update.yml` file for local testing. ## Quick Example ```bash npm install electron-updater ``` ```toml # src-tauri/Cargo.toml [dependencies] tauri-plugin-updater = "2" ```
skilldb get desktop-app-skills/Auto UpdateFull skill: 346 linesAuto-Update — Desktop Apps
You are an expert in implementing auto-update mechanisms for desktop applications, covering both Electron (electron-updater) and Tauri's built-in updater.
Overview
Auto-update allows desktop applications to check for, download, and install new versions without requiring users to manually download installers. This is critical for shipping fixes, features, and security patches quickly.
Two primary ecosystems:
- Electron: Uses
electron-updater(part of electron-builder) with support for GitHub Releases, S3, and generic HTTP servers - Tauri: Has a built-in updater plugin that checks a JSON endpoint for new versions
Both follow a similar flow: check for update, download in background, prompt or auto-install on restart.
Setup & Configuration
Electron: electron-updater setup
npm install electron-updater
// main.js
const { autoUpdater } = require('electron-updater');
const log = require('electron-log');
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
// Configure update source
autoUpdater.setFeedURL({
provider: 'github',
owner: 'your-org',
repo: 'your-app',
});
// Or use a generic server
// autoUpdater.setFeedURL({
// provider: 'generic',
// url: 'https://updates.yourapp.com/releases',
// });
electron-builder publish config (package.json)
{
"build": {
"appId": "com.yourcompany.yourapp",
"publish": [
{
"provider": "github",
"owner": "your-org",
"repo": "your-app",
"releaseType": "release"
}
],
"mac": {
"target": ["dmg", "zip"],
"notarize": true
},
"win": {
"target": ["nsis"],
"publisherName": "Your Company"
},
"linux": {
"target": ["AppImage", "deb"]
}
}
}
Tauri: updater plugin setup
# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-updater = "2"
// tauri.conf.json
{
"plugins": {
"updater": {
"endpoints": [
"https://updates.yourapp.com/{{target}}/{{arch}}/{{current_version}}"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6...(your public key)"
}
}
}
// src-tauri/src/lib.rs
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Core Patterns
Electron: Full update lifecycle
// updater.js — main process module
const { autoUpdater } = require('electron-updater');
const { BrowserWindow, dialog } = require('electron');
function initAutoUpdater(mainWindow) {
// Check for updates on launch (with delay to not slow startup)
setTimeout(() => {
autoUpdater.checkForUpdates();
}, 5000);
// Also check periodically (every 4 hours)
setInterval(() => {
autoUpdater.checkForUpdates();
}, 4 * 60 * 60 * 1000);
autoUpdater.on('checking-for-update', () => {
sendToRenderer(mainWindow, 'update:status', 'Checking for updates...');
});
autoUpdater.on('update-available', (info) => {
sendToRenderer(mainWindow, 'update:available', {
version: info.version,
releaseDate: info.releaseDate,
releaseNotes: info.releaseNotes,
});
});
autoUpdater.on('update-not-available', () => {
sendToRenderer(mainWindow, 'update:status', 'Up to date');
});
autoUpdater.on('download-progress', (progress) => {
sendToRenderer(mainWindow, 'update:progress', {
percent: Math.round(progress.percent),
transferred: progress.transferred,
total: progress.total,
bytesPerSecond: progress.bytesPerSecond,
});
});
autoUpdater.on('update-downloaded', (info) => {
sendToRenderer(mainWindow, 'update:ready', {
version: info.version,
});
});
autoUpdater.on('error', (err) => {
sendToRenderer(mainWindow, 'update:error', err.message);
});
}
function sendToRenderer(win, channel, data) {
if (win && !win.isDestroyed()) {
win.webContents.send(channel, data);
}
}
function installUpdate() {
autoUpdater.quitAndInstall(false, true);
}
module.exports = { initAutoUpdater, installUpdate };
Electron: Renderer UI for updates
// renderer.js
window.api.on('update:available', (info) => {
showBanner(`Version ${info.version} is available. Downloading...`);
});
window.api.on('update:progress', (progress) => {
updateProgressBar(progress.percent);
});
window.api.on('update:ready', (info) => {
showDialog({
title: 'Update Ready',
message: `Version ${info.version} has been downloaded. Restart to apply?`,
buttons: ['Restart Now', 'Later'],
onConfirm: () => window.api.send('update:install'),
});
});
window.api.on('update:error', (errorMsg) => {
console.error('Update error:', errorMsg);
});
Electron: Differential / delta updates
{
"build": {
"nsis": {
"differentialPackage": true
},
"mac": {
"target": [
{ "target": "dmg" },
{ "target": "zip" }
]
}
}
}
Tauri: Update check from frontend
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
async function checkForUpdates() {
const update = await check();
if (update) {
console.log(`Update available: ${update.version}`);
console.log(`Release notes: ${update.body}`);
// Download and install
let downloaded = 0;
let contentLength = 0;
await update.downloadAndInstall((event) => {
switch (event.event) {
case 'Started':
contentLength = event.data.contentLength ?? 0;
console.log(`Download started, size: ${contentLength}`);
break;
case 'Progress':
downloaded += event.data.chunkLength;
const percent = contentLength
? Math.round((downloaded / contentLength) * 100)
: 0;
updateProgressBar(percent);
break;
case 'Finished':
console.log('Download complete');
break;
}
});
// Restart the app to apply
await relaunch();
} else {
console.log('No update available');
}
}
Tauri: Update server response format
The endpoint must return JSON in this format:
{
"version": "1.2.0",
"notes": "Bug fixes and performance improvements",
"pub_date": "2025-01-15T12:00:00Z",
"platforms": {
"windows-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"url": "https://releases.yourapp.com/v1.2.0/app_1.2.0_x64-setup.nsis.zip"
},
"darwin-aarch64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"url": "https://releases.yourapp.com/v1.2.0/app_1.2.0_aarch64.app.tar.gz"
},
"linux-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"url": "https://releases.yourapp.com/v1.2.0/app_1.2.0_amd64.AppImage.tar.gz"
}
}
}
Staging / beta channel updates
// Electron: channel-based updates
autoUpdater.channel = 'beta'; // checks beta releases on GitHub
// Or dynamically switch channels
function setUpdateChannel(channel) {
autoUpdater.channel = channel; // 'stable', 'beta', 'alpha'
autoUpdater.checkForUpdates();
}
Best Practices
- Delay the first update check by several seconds after app launch to avoid slowing startup.
- Always show download progress to the user so they know something is happening.
- Let the user choose when to restart rather than force-quitting the app.
- Sign all update artifacts. Tauri requires signature verification by default. For Electron, code-sign your installers.
- Use differential/delta updates when possible to reduce download sizes.
- Implement a beta channel for early adopters to test before rolling out to all users.
- Log all update events for debugging failed updates in production.
- Handle network errors gracefully — update failures should never crash the app.
Common Pitfalls
- Not code-signing on macOS: macOS Gatekeeper will block unsigned updates. Configure notarization in your build pipeline.
- Testing updates in development:
autoUpdaterdoesn't work in dev mode. UseautoUpdater.forceDevUpdateConfig = truewith adev-app-update.ymlfile for local testing. - Forgetting to publish release assets: The updater looks for specific file patterns (e.g.,
latest.yml,latest-mac.yml). If these are missing from your release, the update silently fails. - Race conditions with quit-and-install: If the user triggers actions while the update is installing, data can be lost. Disable UI interactions once install begins.
- Ignoring rollback: If an update introduces a critical bug, have a plan. Consider keeping the previous version's installer accessible.
- Large updates on metered connections: Consider detecting metered connections and prompting the user before downloading large updates.
Core Philosophy
Auto-update is a trust contract between you and your users. They have given your application permission to modify itself on their machine, and in return you must ensure that updates are safe, non-disruptive, and reversible. Every update should be signed, verified, and tested before it reaches users. A broken update that crashes on launch with no rollback path is the fastest way to lose user trust permanently.
The user should always feel in control of the update process. Download in the background, show progress when relevant, and let the user choose when to restart. Force-quitting the application to install an update while the user is in the middle of work is hostile. The best update experience is one where the user barely notices it happened — they see a subtle "restart to update" prompt and find everything working after restart.
Treat updates as a deployment pipeline, not a single event. Build, sign, upload to a release channel, test on a beta group, promote to stable, monitor error rates. If errors spike after a release, have a one-click rollback ready. The mechanical act of shipping bits is easy; the operational discipline around safe delivery is what separates amateur desktop apps from professional ones.
Anti-Patterns
-
Force-quitting the app to install — calling
quitAndInstallwithout user consent while work is in progress leads to data loss and user frustration; always prompt and let the user choose when to restart. -
Checking for updates on every app launch synchronously — blocking the startup sequence with an update check adds seconds of latency before the user can interact; delay the first check and run it in the background.
-
Shipping unsigned updates — both macOS Gatekeeper and Windows SmartScreen block unsigned binaries; skipping code signing makes updates fail silently or show frightening OS warnings.
-
Having no rollback mechanism — deploying a broken update with no way to revert means users are stuck until a hotfix ships; always keep the previous version accessible for quick rollback.
-
Ignoring update errors silently — swallowing errors from the update process without logging or user notification means broken updates go undetected until users report them manually.
Install this skill directly: skilldb add desktop-app-skills
Related Skills
Electron Ipc
IPC communication patterns for Electron main and renderer process messaging
Electron
Electron fundamentals for building cross-platform desktop applications with web technologies
File System Access
File system access patterns, native dialogs, and drag-and-drop for desktop applications
Native Menus
System tray icons, native application menus, and desktop notifications for Electron and Tauri apps
Packaging
Building, packaging, and distributing desktop applications for Windows, macOS, and Linux
Security
Desktop application security including context isolation, preload scripts, CSP, and sandboxing for Electron and Tauri