# How Tauri's Self-Updater Works: Architecture and Complete Setup Guide

> Discover how Tauri's self-updater works with its architecture and get a complete setup guide. Learn about its signed incremental update system and remote endpoint querying.

- Repository: [Tauri/tauri](https://github.com/tauri-apps/tauri)
- Tags: how-to-guide
- Published: 2026-02-26

---

**Tauri's self-updater is a built-in, signed, incremental update system that queries remote endpoints, verifies cryptographic signatures, and replaces the running application with platform-specific bundles.**

The **tauri-apps/tauri** repository provides a robust self-updater that enables desktop applications to automatically download and install updates with cryptographic verification. This system integrates seamlessly with the Tauri build pipeline to create signed update bundles for Windows, macOS, and Linux. Understanding how the **Tauri self-updater** works allows you to deploy secure, automated updates to your users without relying on external package managers.

## Architecture of the Tauri Self-Updater

The self-updater consists of three coordinated components defined in the source code: the configuration schema, the bundler that creates update artifacts, and the runtime plugin that handles installation.

### Configuration Layer (UpdaterConfig)

The updater behavior is controlled by the `UpdaterConfig` struct defined in [`crates/tauri-utils/src/config_v1/mod.rs`](https://github.com/tauri-apps/tauri/blob/main/crates/tauri-utils/src/config_v1/mod.rs) (lines 2442-2549). This configuration requires:

- **`active`**: Boolean flag to enable the updater
- **`pubkey`**: Public RSA key for signature verification (required when active)
- **`endpoints`**: Array of HTTPS URLs supporting template variables like `{{current_version}}`, `{{target}}`, and `{{arch}}`
- **`dialog`**: Boolean to enable the built-in native dialog (set to `false` for custom UIs)

The configuration is parsed by the CLI interface in [`crates/tauri-cli/src/interface/rust.rs`](https://github.com/tauri-apps/tauri/blob/main/crates/tauri-cli/src/interface/rust.rs) (lines 832-839) and injected into the application at compile time.

### Bundle Generation (bundle_project)

When you run `tauri build --bundles updater`, the bundler executes the `bundle_project` function in [`crates/tauri-bundler/src/bundle/updater_bundle.rs`](https://github.com/tauri-apps/tauri/blob/main/crates/tauri-bundler/src/bundle/updater_bundle.rs) (lines 28-41). This creates platform-specific archives:

- **Windows**: ZIP archive containing the MSI or NSIS installer
- **macOS**: TAR.GZ archive containing the `.app` bundle
- **Linux**: TAR.GZ archive containing the AppImage or binary

The bundler dispatches to platform-specific implementations (`bundle_update_windows`, `bundle_update_macos`, `bundle_update_linux`) and automatically signs the artifacts if a private key is present.

### Runtime API and Event System

The `@tauri-apps/plugin-updater` package exposes the JavaScript API that communicates with the core runtime. Key methods include:

- `checkUpdate()`: Queries the configured endpoints for a new version
- `installUpdate()`: Downloads, verifies, and installs the update
- `onUpdaterEvent()`: Listens to lifecycle events like `download-progress`

Behind the scenes, the runtime emits events via `app.emit_to("updater", ...)` as implemented in [`crates/tauri/src/lib.rs`](https://github.com/tauri-apps/tauri/blob/main/crates/tauri/src/lib.rs) (lines 979-983), allowing both the built-in dialog and custom UIs to react to update states.

## Setting Up the Tauri Self-Updater

Follow these steps to enable signed automatic updates in your Tauri application.

### Install the Updater Plugin

Add the official updater plugin to your frontend dependencies:

```bash
npm install @tauri-apps/plugin-updater

```

For Tauri v2, this plugin replaces the legacy core updater. Ensure your [`tauri.conf.json`](https://github.com/tauri-apps/tauri/blob/main/tauri.conf.json) uses the `plugins.updater` configuration key.

### Configure tauri.conf.json

Add the updater configuration to your [`tauri.conf.json`](https://github.com/tauri-apps/tauri/blob/main/tauri.conf.json) file:

```json
{
  "plugins": {
    "updater": {
      "active": true,
      "dialog": false,
      "endpoints": [
        "https://mycdn.com/{{target}}/{{arch}}/latest.json"
      ],
      "pubkey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqh..."
    }
  }
}

```

The `pubkey` field must contain the public portion of your signing key pair. The CLI validates this configuration during the build process.

### Generate Cryptographic Signing Keys

Create a secure key pair for signing update bundles:

```bash
tauri signer generate

```

This command, implemented in [`crates/tauri-cli/src/signer/generate.rs`](https://github.com/tauri-apps/tauri/blob/main/crates/tauri-cli/src/signer/generate.rs), creates a private key saved to `$HOME/.tauri-updater.key` and prints the public key to stdout. Store the private key securely in your CI environment and paste the public key into [`tauri.conf.json`](https://github.com/tauri-apps/tauri/blob/main/tauri.conf.json).

### Build Signed Update Bundles

Generate the update artifact for your target platform:

```bash
tauri build --bundles updater

```

The bundler creates the platform-specific archive (ZIP for Windows, TAR.GZ for macOS/Linux) and signs it using your private key. The signature is embedded in the bundle metadata and required for the updater to accept the installation.

### Deploy the Update Manifest

Host a JSON manifest at your configured endpoint containing:

```json
{
  "version": "1.2.3",
  "notes": "Bug fixes and performance improvements",
  "pub_date": "2026-02-26T12:00:00Z",
  "url": "https://mycdn.com/windows/appname-updater.zip",
  "signature": "MEUCIQC..."
}

```

The `signature` field contains the Base64-encoded cryptographic signature generated during the build process. The updater downloads the bundle from `url` only if the version is newer than the running application and the signature validates against your configured public key.

### Implement the Frontend Integration

Integrate the updater into your UI using the plugin API:

```typescript
import { checkUpdate, installUpdate, onUpdaterEvent } from '@tauri-apps/plugin-updater';

async function checkForUpdates() {
  const update = await checkUpdate();
  
  if (update) {
    const shouldInstall = confirm(`Version ${update.version} is available. Install now?`);
    if (shouldInstall) {
      await installUpdate();
    }
  }
}

// Display download progress
onUpdaterEvent('download-progress', ({ progress }) => {
  console.log(`Downloading: ${progress}%`);
  document.getElementById('progress-bar').style.width = `${progress}%`;
});

```

If you set `"dialog": true` in the configuration, you can alternatively call `update()` from the plugin to display the built-in native dialog that handles the entire flow automatically.

## Summary

- **Tauri's self-updater** uses a three-tier architecture: configuration in `UpdaterConfig`, bundling via `bundle_project`, and runtime APIs exposed through `@tauri-apps/plugin-updater`.
- **Cryptographic signing** is mandatory; generate keys with `tauri signer generate` and store the public key in [`tauri.conf.json`](https://github.com/tauri-apps/tauri/blob/main/tauri.conf.json).
- **Platform bundles** differ by OS: Windows uses ZIP archives, while macOS and Linux use TAR.GZ.
- **Template variables** in endpoint URLs (`{{target}}`, `{{arch}}`, `{{current_version}}`) allow dynamic routing to platform-specific manifests.
- **Event-driven UI** updates are possible via `onUpdaterEvent`, supporting custom progress bars and installation workflows.

## Frequently Asked Questions

### How does Tauri verify update authenticity?

The Tauri self-updater verifies updates using **RSA public-key cryptography**. When `installUpdate()` runs, the runtime downloads the bundle and validates its signature against the `pubkey` defined in [`tauri.conf.json`](https://github.com/tauri-apps/tauri/blob/main/tauri.conf.json) (as specified in [`crates/tauri-utils/src/config_v1/mod.rs`](https://github.com/tauri-apps/tauri/blob/main/crates/tauri-utils/src/config_v1/mod.rs)). If the signature verification fails, the update is rejected and the application continues running the current version.

### What bundle formats does the Tauri self-updater support?

The bundler in [`crates/tauri-bundler/src/bundle/updater_bundle.rs`](https://github.com/tauri-apps/tauri/blob/main/crates/tauri-bundler/src/bundle/updater_bundle.rs) produces **ZIP archives for Windows** and **TAR.GZ archives for macOS and Linux**. Windows packages contain MSI or NSIS installers, macOS packages contain `.app` bundles, and Linux packages contain AppImages or standalone binaries. These formats are hardcoded in the `bundle_project` dispatch logic to ensure compatibility with the installation mechanisms of each operating system.

### Can I use a custom update server instead of a static JSON file?

Yes. The `endpoints` configuration accepts any HTTPS URL that returns the required JSON manifest format. You can implement dynamic server-side logic to handle the template variables (`{{current_version}}`, `{{target}}`, `{{arch}}`) and return conditional responses based on request headers, query parameters, or user authentication. The runtime simply performs a GET request and parses the JSON response.

### Is the built-in dialog required, or can I build a custom UI?

You can build a completely custom UI by setting `"dialog": false` in the updater configuration. This disables the native dialog and allows you to use `checkUpdate()`, `installUpdate()`, and `onUpdaterEvent()` to create bespoke update flows with custom styling, progress indicators, and user prompts. The built-in dialog is optional convenience, not a requirement.