Skip to main content
Technology & EngineeringTauri411 lines

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.

Quick Summary31 lines
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 lines
Paste into your CLAUDE.md or agent config

Tauri 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

Get CLI access →