Tauri Distribution
Distributing Tauri applications including installers for MSI, DMG, AppImage, and deb, auto-update with the built-in updater, code signing for Windows and macOS, CI/CD builds, and cross-compilation.
You are an expert in distributing Tauri 2.0 applications across all desktop platforms, including packaging, code signing, auto-updates, and CI/CD pipelines. ## Key Points - **Distributing unsigned binaries**: Users get scary warnings on Windows and cannot open the app at all on macOS Gatekeeper. - **Skipping notarization on macOS**: Gatekeeper quarantine will block your app for most users without notarization. - **Manual builds for releases**: CI/CD ensures consistent, reproducible builds across all platforms. - **Hosting updates on insecure endpoints**: Update manifests and binaries must be served over HTTPS with signature verification. - **Building release binaries with debug features**: Debug builds include devtools and verbose logging. Always use release profile for distribution. - **Use CI/CD** with matrix builds on each target OS - **macOS universal binaries** (Intel + Apple Silicon) can be built on a single macOS runner with `--target universal-apple-darwin` - **Linux ARM** builds require either an ARM runner or cross-compilation toolchains - **Windows ARM** is not yet widely supported ## Quick Example ```bash # Build DMG (drag-and-drop install) cargo tauri build --bundles dmg # Build .app bundle only cargo tauri build --bundles app ``` ```bash # PFX/P12 certificate export TAURI_SIGNING_PRIVATE_KEY="path/to/certificate.pfx" export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="password" ```
skilldb get tauri-skills/Tauri DistributionFull skill: 411 linesTauri Distribution — Packaging and Shipping Apps
You are an expert in distributing Tauri 2.0 applications across all desktop platforms, including packaging, code signing, auto-updates, and CI/CD pipelines.
Core Philosophy
Distribution is not an afterthought. The decisions you make about installer type, signing strategy, and update mechanism affect user experience from the first download. Tauri produces small binaries by default, but the distribution pipeline -- signing, notarization, update hosting -- requires deliberate setup.
Ship signed binaries on every platform. Unsigned apps trigger security warnings on Windows and are blocked entirely on macOS by default. Auto-update from day one, because manual update distribution does not scale. Use CI/CD to build for all platforms from a single commit, ensuring reproducibility and eliminating "works on my machine" distribution bugs.
Anti-Patterns
- Distributing unsigned binaries: Users get scary warnings on Windows and cannot open the app at all on macOS Gatekeeper.
- Skipping notarization on macOS: Gatekeeper quarantine will block your app for most users without notarization.
- Manual builds for releases: CI/CD ensures consistent, reproducible builds across all platforms.
- Hosting updates on insecure endpoints: Update manifests and binaries must be served over HTTPS with signature verification.
- Building release binaries with debug features: Debug builds include devtools and verbose logging. Always use release profile for distribution.
Installer Types
Windows
# Build NSIS installer (recommended -- modern, smaller)
cargo tauri build --bundles nsis
# Build MSI installer (enterprise deployments)
cargo tauri build --bundles msi
# Build both
cargo tauri build --bundles nsis,msi
Configuration for Windows bundles:
// tauri.conf.json
{
"bundle": {
"targets": ["nsis", "msi"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com",
"nsis": {
"displayLanguageSelector": false,
"installerIcon": "icons/icon.ico",
"languages": ["English"],
"startMenuFolder": "My App",
"headerImage": "icons/nsis-header.bmp",
"sidebarImage": "icons/nsis-sidebar.bmp"
}
}
}
}
macOS
# Build DMG (drag-and-drop install)
cargo tauri build --bundles dmg
# Build .app bundle only
cargo tauri build --bundles app
{
"bundle": {
"targets": ["dmg", "app"],
"macOS": {
"minimumSystemVersion": "10.15",
"frameworks": [],
"dmg": {
"appPosition": { "x": 180, "y": 170 },
"applicationFolderPosition": { "x": 480, "y": 170 },
"windowSize": { "width": 660, "height": 400 }
}
}
}
}
Linux
# Build AppImage (universal, no install required)
cargo tauri build --bundles appimage
# Build Debian package
cargo tauri build --bundles deb
# Build RPM package
cargo tauri build --bundles rpm
# Build all Linux formats
cargo tauri build --bundles appimage,deb,rpm
{
"bundle": {
"targets": ["appimage", "deb", "rpm"],
"linux": {
"deb": {
"depends": ["libwebkit2gtk-4.1-0", "libayatana-appindicator3-1"],
"section": "utility",
"priority": "optional"
},
"rpm": {
"release": "1",
"epoch": 0
}
}
}
}
Code Signing
macOS Code Signing and Notarization
# Required environment variables
export APPLE_CERTIFICATE="$(cat certificate.p12 | base64)"
export APPLE_CERTIFICATE_PASSWORD="your-password"
export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)"
# For notarization (required for distribution outside App Store)
export APPLE_API_KEY="path/to/AuthKey_XXXXX.p8"
export APPLE_API_KEY_ID="XXXXX"
export APPLE_API_ISSUER="issuer-uuid"
# Build with signing and notarization
cargo tauri build
Windows Code Signing
Using a certificate file:
# PFX/P12 certificate
export TAURI_SIGNING_PRIVATE_KEY="path/to/certificate.pfx"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="password"
Using Windows Certificate Store:
{
"bundle": {
"windows": {
"certificateThumbprint": "A1B2C3D4E5F6...",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
}
}
EV Code Signing (Extended Validation)
EV certificates on USB tokens require special handling:
# Windows -- use signtool directly
# The Tauri build will attempt to sign; for EV, use a custom signing script
# Set certificate thumbprint and the signing will use the hardware token
Auto-Update System
Setup
# Install the updater plugin
cargo add tauri-plugin-updater
npm install @tauri-apps/plugin-updater
# Generate signing keys
cargo tauri signer generate -w ~/.tauri/myapp.key
# Save the public key -- it goes in tauri.conf.json
# Save the private key securely -- it signs updates in CI
Configuration
// tauri.conf.json
{
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"endpoints": [
"https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}"
],
"windows": {
"installMode": "passive"
}
}
}
}
Update Endpoint
Your server must return a JSON response:
{
"version": "1.2.0",
"notes": "What's new in 1.2.0:\n- Fixed crash on startup\n- New dark mode",
"pub_date": "2025-03-15T10:00:00Z",
"platforms": {
"windows-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"url": "https://releases.myapp.com/downloads/v1.2.0/myapp_1.2.0_x64-setup.nsis.zip"
},
"darwin-aarch64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"url": "https://releases.myapp.com/downloads/v1.2.0/myapp.app.tar.gz"
},
"darwin-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"url": "https://releases.myapp.com/downloads/v1.2.0/myapp.app.tar.gz"
},
"linux-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6...",
"url": "https://releases.myapp.com/downloads/v1.2.0/myapp_1.2.0_amd64.AppImage.tar.gz"
}
}
}
Return 204 No Content when no update is available.
Frontend Update Logic
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("No update available");
return;
}
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 = ((downloaded / contentLength) * 100).toFixed(1);
console.log(`Downloaded ${percent}%`);
break;
case "Finished":
console.log("Download complete");
break;
}
});
// Restart the app
await relaunch();
}
Using GitHub Releases as Update Endpoint
{
"plugins": {
"updater": {
"endpoints": [
"https://github.com/yourname/yourapp/releases/latest/download/latest.json"
]
}
}
}
Generate latest.json in CI after building:
# The Tauri build generates .sig files for each bundle
# Create latest.json from the build artifacts
CI/CD Pipeline
GitHub Actions
name: Build and Release
on:
push:
tags: ["v*"]
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: ubuntu-22.04
target: x86_64-unknown-linux-gnu
- platform: macos-latest
target: aarch64-apple-darwin
- platform: macos-latest
target: x86_64-apple-darwin
- platform: windows-latest
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install dependencies (Linux)
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install frontend dependencies
run: npm ci
- name: Build Tauri
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
with:
tagName: ${{ github.ref_name }}
releaseName: "v__VERSION__"
releaseBody: "See the release notes for details."
releaseDraft: true
prerelease: false
args: --target ${{ matrix.target }}
Build Output Locations
src-tauri/target/release/bundle/
├── appimage/ # Linux AppImage
│ └── my-app_1.0.0_amd64.AppImage
├── deb/ # Debian package
│ └── my-app_1.0.0_amd64.deb
├── dmg/ # macOS disk image
│ └── My App_1.0.0_aarch64.dmg
├── macos/ # macOS .app bundle
│ └── My App.app
├── msi/ # Windows MSI
│ └── My App_1.0.0_x64_en-US.msi
└── nsis/ # Windows NSIS
└── My App_1.0.0_x64-setup.exe
Cross-Compilation Notes
True cross-compilation for Tauri is difficult because each platform requires its native toolchain and webview SDK. The recommended approach:
- Use CI/CD with matrix builds on each target OS
- macOS universal binaries (Intel + Apple Silicon) can be built on a single macOS runner with
--target universal-apple-darwin - Linux ARM builds require either an ARM runner or cross-compilation toolchains
- Windows ARM is not yet widely supported
# macOS universal binary
cargo tauri build --target universal-apple-darwin
Install this skill directly: skilldb add tauri-skills
Related Skills
Tauri Commands
Rust commands with the invoke pattern, argument passing, return types, async commands, error handling, state management, and type safety between Rust and TypeScript in Tauri 2.0.
Tauri Frontend
Frontend integration with Tauri 2.0 including React, Vue, Svelte, and Solid frameworks, Vite configuration, asset handling, window management, multiple windows, and webview communication.
Tauri Fundamentals
Tauri 2.0 architecture, Rust backend with webview frontend, project setup with Cargo and npm, development workflow, and build targets for Windows, macOS, Linux, iOS, and Android.
Tauri Mobile
Tauri 2.0 mobile development for iOS and Android, including platform-specific code, mobile plugins, testing on simulators and devices, and app store distribution.
Tauri Patterns
Common Tauri 2.0 patterns: system tray apps, menu bar apps, file handling, SQLite database integration, IPC communication patterns, background tasks, and single-instance enforcement.
Tauri Plugins
Tauri 2.0 plugin system including official plugins for filesystem, shell, dialog, notification, HTTP, clipboard, updater, and deep-link, plus community plugins and writing custom plugins.