Distribution
Guide to distributing your Tauri v2 application to users on Windows, macOS, and Linux, including setting up application updates and auto-updates.
Distribution is the step that turns your finished application into something users can actually install and run. After you build and bundle your Tauri app, you need to choose how to deliver it — as a direct download, through a platform app store, or via a package manager — and you need to decide whether to include an update mechanism so your users stay on the latest version.
This guide covers the full distribution pipeline: configuring and generating the right platform-specific bundles, publishing those bundles where your users can reach them, and then wiring up an auto‑update system so your application can update itself silently in the background.
Distributing Your Application
Getting your application into users’ hands means picking the right format for each operating system and understanding what each platform requires in terms of signing, notarization, and installation experience. Tauri’s build command can produce several bundle types out of the box. You control which ones get generated through the bundle section of your tauri.conf.json file. The Distributing Your Application page expands on channels and hosting.
Choosing Distribution Targets
Before you think about installers, decide who your users are and which platforms they run. Tauri supports Windows (MSI, NSIS installer, Microsoft Store), macOS (DMG, App Bundle, App Store), and Linux (AppImage, Debian package, RPM, Snap, Flatpak, and Arch User Repository). Each format has trade‑offs in complexity, required tooling, and user experience.
A pragmatic starting point for most projects:
- Windows: NSIS installer (
-setup.exe) for direct downloads, or MSI for enterprise environments that need silent installation. - macOS: A DMG file for direct distribution after notarization, or the App Store for discoverability.
- Linux: AppImage as the simplest “download and run” option, plus a Debian package for Ubuntu/Debian users.
You can generate multiple formats in a single build by listing them in the bundle configuration.
Bundling Your Application
The tauri build command compiles your Rust backend, bundles your frontend, and packages everything into the configured formats. You run it from your project root:
npm run tauri build
By default, this compiles and bundles in one step. If you need more granular control — for example, to sign binaries separately or to produce different bundle combinations for separate distribution channels — you can split the process. Build the application without bundling, then run the bundle step with explicit format flags:
# Build without bundling
npm run tauri build -- --no-bundle
# Then bundle only a DMG for macOS
npm run tauri bundle -- --bundles dmg
# Bundle as both AppImage and deb for Linux
npm run tauri bundle -- --bundles appimage,deb
Bundle configuration:
The bundle step reads the bundle object in tauri.conf.json. You can point to an alternate config file with --config if you need different settings for, say, the Mac App Store versus direct distribution.
The bundle targets you specify in the command or config determine what appears in src-tauri/target/release/bundle/. Each platform has its own sub‑directory (e.g., bundle/nsis/, bundle/dmg/, bundle/appimage/).
Platform-Specific Distribution
Each operating system has its own conventions, signing requirements, and installer expectations. The following sections outline what you need to produce a distributable package for Windows, macOS, and Linux.
Windows
Tauri can create two types of Windows installers:
- NSIS installer (
-setup.exe): A traditional setup wizard. Users download a single executable, walk through a few screens, and the application is installed. This is the most common format for consumer software. - MSI installer (
.msi): A Microsoft Installer package. It supports silent installation, group policy deployment, and is preferred in enterprise environments. MSI installers can only be built on a Windows machine; cross‑compilation from macOS or Linux is not supported for MSI.
To generate an installer, you need the WiX Toolset v3 (for MSI) or NSIS (for the setup executable). The Tauri CLI downloads NSIS automatically when needed, but WiX must be installed separately.
To target 32‑bit or ARM64 Windows, add the appropriate Rust target and compile with the --target flag:
# Install the target
rustup target add i686-pc-windows-msvc
# Build for 32-bit
npm run tauri build -- --target i686-pc-windows-msvc
Windows SmartScreen and code signing:
Unsigned executables will trigger a SmartScreen warning on Windows. To avoid this, you must sign your installer and the application binary with an Authenticode certificate. Without signing, users see a “Windows protected your PC” dialog that can discourage installation.
For distribution to the Microsoft Store, Tauri integrates with the store’s packaging format. You need to configure the windows.wix or the store‑specific settings in tauri.conf.json and sign the package with a certificate that Microsoft trusts.
macOS
For macOS, you have two primary distribution paths:
- Direct distribution with a DMG file: You build a
.appbundle, sign it with an Apple Developer ID, submit it for notarization, and then wrap it inside a DMG disk image. Users download the DMG, mount it, and drag the app to their Applications folder. This is the most common approach for apps distributed outside the Mac App Store. - Mac App Store: You submit the signed and packaged application to Apple for review. This gives you discoverability and built‑in updates through the App Store, but requires adhering to App Store guidelines and sandboxing restrictions.
Both paths require you to be a member of the Apple Developer Program. Notarization is mandatory for direct downloads — macOS Gatekeeper will refuse to open your app otherwise.
The build command produces a .app bundle. To get a DMG, include dmg in your bundle targets. Tauri uses create-dmg under the hood; you can customize the DMG’s appearance (background image, icon placement) through the bundle config.
Notarization is not optional:
If you distribute a DMG outside the App Store and skip notarization, users will see a message that the application cannot be opened because the developer cannot be verified. This is a hard block on modern macOS versions. Apple’s xcrun notarytool is the command‑line tool you’ll use, and you’ll need an app‑specific password for your Apple ID.
Linux
Linux distributions have no single package manager, so you often need to provide several formats to cover the user base. Tauri supports:
- AppImage: A self‑contained file that runs on any Linux distribution without installation. Just download, make executable, and run. It bundles the required libraries, including WebKitGTK, so it’s a reliable lowest‑common‑denominator format.
- Debian package (
.deb): Used by Debian, Ubuntu, and derivatives. Users can install it withdpkg -ior through a software center. You can set up a PPA for automatic updates. - RPM package (
.rpm): For Fedora, Red Hat Enterprise Linux, and openSUSE. Installed withrpm -iordnf. - Snap and Flatpak: Sandboxed application distribution platforms that work across distros. Publishing through the Snap Store (Snapcraft) or Flathub gives users automatic updates and sandboxed security. These require additional packaging metadata and a review process.
- Arch User Repository (AUR): For Arch Linux. You can publish a PKGBUILD that downloads the binary from your releases and installs it along with the necessary WebKitGTK dependencies.
No standalone raw binary:
You cannot distribute a plain Linux binary that users simply run without any packaging. Tauri applications depend on system libraries like WebKitGTK and GTK. Even a statically linked binary would not be portable without those runtime libraries. AppImage is the closest you can get to a single‑file executable that works everywhere.
To build a Debian package and an AppImage in one go, you configure:
{
"bundle": {
"linux": {
"formats": ["deb", "appimage"]
}
}
}
The resulting bundles will be in src-tauri/target/release/bundle/deb/ and .../appimage/.
Cloud Distribution Services
If you want to offload the infrastructure for hosting updates and delivering downloads, services like CrabNebula Cloud provide Tauri‑specific distribution with auto‑update support built in. You upload your signed builds, and the platform generates update manifests, serves the binaries over a CDN, and tracks download metrics. This can simplify the entire update pipeline, especially if you don’t want to manage GitHub Releases and your own manifest generation.
Application Updates
Once users have installed your application, you need a way to get improvements, bug fixes, and security patches onto their machines. An application without an update mechanism decays quickly — users get stuck on old versions, report already‑fixed bugs, and miss critical security fixes. See Application Updates.
Why Updates Matter
Desktop applications are not like websites where you deploy a new version and every visitor instantly sees it. Users install a snapshot and, without an update mechanism, that snapshot remains frozen. Automatic updates close that gap. They let you ship fixes rapidly, roll back dangerous changes if needed, and keep your entire user base on a consistent version with minimal user effort.
Update Strategies
There are two broad strategies for updating a Tauri app:
- Manual updates: You notify the user that a new version is available (perhaps through an in‑app banner or a download link on your website), and the user downloads and re‑installs the application themselves. This is simple but has poor adoption — most users will ignore the notification, especially if it requires leaving the app.
- Automatic updates: The application checks for new versions, downloads the update in the background, and either installs it immediately or applies it the next time the app restarts. Tauri’s
tauri-plugin-updaterprovides the building blocks for this approach, and it is the recommended way to keep your app current.
Version Management
Tauri reads the application version from tauri.conf.json in the version field. This should match the version you use for your update manifests and release tags. If you omit the version in tauri.conf.json, Tauri falls back to the package.version from your src-tauri/Cargo.toml, but explicitly setting it in the config file keeps the version definition in one predictable place.
Semantic versioning (e.g., 1.2.3) is the standard. The updater plugin can be configured to treat different version increments differently — for example, skipping minor updates if a major version bump requires user acknowledgement.
Auto Updates
Tauri’s auto‑update system is built around the tauri-plugin-updater plugin. It uses a public‑key signature to verify that the update package has not been tampered with, and it relies on an external update server (typically GitHub Releases or a cloud service) to host the update manifest and binary files. The Auto Updates page is the end-to-end setup.
The core flow:
- On app startup (or periodically), the plugin fetches a JSON manifest from a URL you configure.
- It compares the manifest’s version against the current running version.
- If a newer version is found, the plugin checks the digital signature.
- The user (or your custom logic) triggers the download, which pulls the appropriate platform‑specific installer or binary patch.
- The plugin applies the update — on Windows and Linux it launches the new installer; on macOS it replaces the
.appbundle.
Setting Up tauri-plugin-updater
You need to add the plugin on both the Rust side and the JavaScript side.
Rust Setup
Add the plugin to your src-tauri/Cargo.toml:
[dependencies]
tauri-plugin-updater = "2"
Then register it in your main entry point, typically src-tauri/src/lib.rs:
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
JavaScript Setup
Install the frontend package:
npm install @tauri-apps/plugin-updater
Now you can import and use it in your React components. The plugin exposes check() and downloadAndInstall(). Here is a minimal component that checks for an update on app start and prompts the user:
import { check } from "@tauri-apps/plugin-updater";
import { useEffect, useState } from "react";
function UpdateChecker() {
const [updateAvailable, setUpdateAvailable] = useState(false);
const [downloading, setDownloading] = useState(false);
useEffect(() => {
async function lookForUpdate() {
try {
const update = await check();
if (update) {
setUpdateAvailable(true);
}
} catch (error) {
console.error("Failed to check for updates:", error);
}
}
lookForUpdate();
}, []);
const handleInstall = async () => {
setDownloading(true);
try {
// The updater plugin handles the download and installation.
// On success, the app will restart or the installer will run.
await check(); // re-check to get the update object
const update = await check(); // The object is needed for downloadAndInstall
if (update) {
await update.downloadAndInstall();
}
} catch (error) {
console.error("Update failed:", error);
}
setDownloading(false);
};
if (!updateAvailable) return null;
return (
<div style={{ padding: "1rem", background: "#e0f0ff" }}>
<p>A new version is available.</p>
<button onClick={handleInstall} disabled={downloading}>
{downloading ? "Downloading..." : "Update Now"}
</button>
</div>
);
}
export default UpdateChecker;
The update object is consumed once:
The check() function returns an update object that contains the metadata and download URL. That object can be used only once for downloadAndInstall(). If you need to show the version number in the UI before the user clicks, store the metadata separately before calling install.
Configuration
Open src-tauri/tauri.conf.json and add the updater configuration inside the plugins object. This example uses GitHub Releases as the update source:
{
"plugins": {
"updater": {
"endpoints": [
"https://github.com/your-username/your-repo/releases/latest/download/latest.json"
],
"pubkey": "YOUR_PUBLIC_KEY_HERE"
}
}
}
The endpoints array lists one or more URLs that return the update manifest JSON. The pubkey is the public key that verifies the manifest’s signature.
Generating Signing Keys
Auto‑update security relies on asymmetric cryptography. You generate a key pair, keep the private key secret (used only during your CI to sign the update manifest), and embed the public key in your application. The updater plugin refuses any update whose manifest signature does not match the public key.
Generate a key pair using the Tauri CLI:
npm run tauri signer generate -- -w ~/.tauri/myapp.key
This command writes the private key to ~/.tauri/myapp.key and prints the public key to the terminal. Copy the public key into your tauri.conf.json as shown above. The private key file should never be committed to version control.
Never expose the private key:
If an attacker obtains your private key, they can sign a malicious update manifest and trick your application into downloading arbitrary code. Treat the private key with the same care as you would a production database password. Store it in an encrypted secret manager in your CI pipeline.
Integration with GitHub Releases
GitHub Releases is a free, reliable host for your installers and update manifest. The workflow looks like this:
- Tag a new version in your repository (e.g.,
v1.2.0). - Build your app for all platforms and generate the bundle files (
.msi,.dmg,.AppImage, etc.). - Create a release using GitHub Actions or manually, attaching the bundle files.
- Generate an update manifest (
latest.json) and upload it to the release, or host it at a stable URL. - Sign the manifest with your private key and include the signature in the JSON.
The manifest structure:
{
"version": "1.2.0",
"notes": "Bug fixes and performance improvements",
"pub_date": "2026-07-09T12:00:00Z",
"platforms": {
"windows-x86_64": {
"signature": "Content of the signature file...",
"url": "https://github.com/your-username/your-repo/releases/download/v1.2.0/app_1.2.0_x64-setup.nsis.zip"
},
"darwin-x86_64": {
"signature": "...",
"url": "https://github.com/your-username/your-repo/releases/download/v1.2.0/App_1.2.0_x64.dmg"
},
"linux-x86_64": {
"signature": "...",
"url": "https://github.com/your-username/your-repo/releases/download/v1.2.0/app_1.2.0_amd64.AppImage.tar.gz"
}
}
}
The signature field contains the output of running tauri signer sign against the manifest. The updater plugin will fetch this manifest, verify that the signature matches the embedded public key, and then download the appropriate platform’s URL.
Verify your setup:
After setting up the manifest and uploading bundles, test the end‑to‑end flow by running your app in release mode, pointing it at the manifest URL. If everything is correct, the app should detect the update, download the installer, and apply it. A successful test confirms that your build pipeline and signing are correctly wired.
Automating with GitHub Actions
You can automate the entire release process — building, signing, creating a GitHub Release, generating the manifest, and signing it — in a single workflow. This reduces manual errors and ensures every release follows the same trusted pipeline. The private key should be stored as a GitHub secret and only injected during the signing step.
Phased Rollouts and Canary Releases
For applications with a large user base, pushing an update to everyone at once is risky. A phased rollout delivers the update to a percentage of users initially, allowing you to monitor crash rates and feedback before expanding. Tauri’s updater does not have built‑in percentage‑based rollout, but you can implement it server‑side: your manifest endpoint returns the new version only to a fraction of requests based on an identifier.
Similarly, you can create a separate “canary” channel: a second manifest URL that early adopters or testers subscribe to. Your app could provide a toggle in settings to switch between the stable and canary channels, enabling prerelease testing without affecting the general user base.
Security Considerations
- Always verify signatures. The updater plugin does this automatically if you configure the public key. Never disable signature verification for production builds.
- Use HTTPS for all endpoints. Man-in-the-middle attacks on the manifest or binary download URLs can redirect users to malicious files.
- Sign your application bundles with platform‑specific code signing certificates (Authenticode on Windows, Apple Developer ID on macOS) in addition to the updater’s manifest signature. This prevents OS‑level warnings and adds another layer of trust.
- Validate the manifest content. Even if the signature is valid, ensure the version field is what you expect. Avoid accepting downgrades unless you intentionally support rollbacks.
Summary
Distribution is not just about creating an installer — it’s about designing the entire update story for your users. Start by picking the bundle formats that match your audience’s platforms and technical expectations. Then, before you ship the first release, set up the auto‑update infrastructure. Integrating tauri-plugin-updater with GitHub Releases gives you a free, secure, and maintainable update pipeline. Sign your binaries at the OS level and your manifest at the application level so that every link in the chain is verified. With these pieces in place, every user who installs your application can stay on the latest, safest version without lifting a finger.
Distributing Your Application
Package, sign, and share your Tauri v2 app with users on Windows, macOS, and Linux using installers, portable builds, website hosting, and GitHub Releases.
Application Updates
How to manage versioning, plan your update strategy, and prepare releases in Tauri v2 so users always run the latest version of your desktop application
Auto Updates in Tauri v2
Implement automatic updates for your Tauri v2 desktop app using the updater plugin, from key generation and signing to deployment with static JSON or a dynamic server.