How to make automatic updates work with Tauri v2 and GitHub

Tauri is a great framework for building desktop applications with web technologies, backed by Rust. An updater comes packed with Tauri and is pretty straightforward to use. The Tauri v1 docs cover it quite nicely. But there are some changes in Tauri v2 that might make it a bit confusing to set up the updater. Here’s how I made it work for my Tauri v2 app using GitHub.

It just works.

TL;DR

The developer side is a manual process:

  1. You build the binaries locally.
  2. Then create a release on GitHub and upload the binaries.
  3. Then update the latest.json file in your GitHub repository or GitHub gist with the new version, release notes, signature and download URL.

Alternatively, you can use GitHub Actions to build the binaries and update the latest.json file automatically. More on that later.

The user side is fully automatic:

  1. When the app starts, it will ping the latest.json file to check for updates, and show a dialog box if an update is available. Or the user can click on the “Check for Updates” button to manually check for updates.
  2. The app will download the new binaries, verify the signature, and replace the old binaries with the new ones. It will then restart to apply the update.

1. The Groundwork

The layout:

  • Local: Insert the updater code into your app’s source code. Then build the distributable binaries on your machine.
  • GitHub: Publish the binaries on GitHub in a release in the project’s repository (or a separate public repository, if the project is private). Update the publicly accessible latest.json file with the new version details.
  • User: The app will ping the latest.json file to check for updates and show a dialog box if an update is available. It will then auto-install the update and restart the app.

Things to do:

  • Make sure you have a working Tauri v2 project. Follow the official docs to see how.
  • Set the same project version in the Cargo.toml, tauri.conf.json and package.json files.
  • Make sure you have a GitHub repository for your project.
    • If your project repo is private, create a new public repository where you will publish your releases.

2. Add Updater to your Project

  1. Add the updater plugin to your project.
1
2
3
[dependencies]
tauri-plugin-updater = { version = "2.0.0-beta", features = ["rustls-tls"] }
tauri-plugin-dialog = "2.0.0-beta"
  1. Install the npm packages.
1
npm install @tauri-apps/plugin-updater @tauri-apps/plugin-dialog
  1. Create private and public keys for the updater. Store the private key and the password in a secure place, maybe even a password manager like Bitwarden. Remember the path to the private key file and the password for the private key. You will need them for Step 7.
1
2
3
4
# On macOS or Linux
npm run tauri signer generate -- -w ~/.tauri/myapp.key
# On Windows
npm run tauri signer generate -- -w $HOME/.tauri/myapp.key
  1. Add the code for the updater to your front-end. You can do it however you like. Most simply:
  • Add a checkForAppUpdates function in your app.
  • Add a “check for updates” button somewhere in your app that calls the above function. Also, automatically call this function when the app loads.
  • This function will check for an update and then show a dialog box if an update is available. The user can then choose to update or not.
  • If the user clicked on the check for updates button, show another dialog box if the update is not available. (Make sure to pass true to the checkForAppUpdates function below in this case.)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// sample front-end code for the updater
import { check } from '@tauri-apps/plugin-updater';
import { ask, message } from '@tauri-apps/plugin-dialog';

async function checkForAppUpdates(onUserClick: false) {
  const update = await check();
  if (update === null) {
			await message('Failed to check for updates.\nPlease try again later.', { 
				title: 'Error',
				kind: 'error',
				okLabel: 'OK'
			});
			return;
		} else if (update?.available) {
    const yes = await ask(`Update to ${update.version} is available!\n\nRelease notes: ${update.body}`, { 
      title: 'Update Available',
      kind: 'info',
      okLabel: 'Update',
      cancelLabel: 'Cancel'
    });
    if (yes) {
      await update.downloadAndInstall();
      // Restart the app after the update is installed by calling the Tauri command that handles restart for your app
      // It is good practice to shut down any background processes gracefully before restarting
      // As an alternative, you could ask the user to restart the app manually
      await invoke("graceful_restart");
    }
  } else if (onUserClick) {
    await message('You are on the latest version. Stay awesome!', { 
      title: 'No Update Available',
      kind: 'info',
      okLabel: 'OK'
    });
  }
}
  1. Make sure to add the relevant permissions in your capabilities > main.json file.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// capabilties > main.json
{
  ...
  "permissions": [
    ...
    "dialog:default",
    "dialog:allow-ask",
    "dialog:allow-message",
    "updater:default",
    "updater:allow-check",
    "updater:allow-download-and-install"
  ]
}
  1. In your tauri.conf.json, add the updater configuration. Make sure to add the raw URL to the endpoints array in the config.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// tauri.conf.json
  {
    "productName": "MY COOL APP",
    "version": "0.1.0",
    ...
    "plugins": {
      "updater": {
        "endpoints": ["RAW GITHUB URL OF THE latest.json FILE OR RAW URL OF THE GITHUB GIST"],
        "pubkey": "THE_PUBLIC_KEY_GENERATED_BY_UPDATER"
      }
    }
  }
  1. Add the required environment variables to your shell before you build the binaries:
  • TAURI_SIGNING_PRIVATE_KEY - The path to the private key file or the private key itself.
  • TAURI_SIGNING_PRIVATE_KEY_PASSWORD - The password for the private key.

Here is how you do it:

1
2
3
export TAURI_SIGNING_PRIVATE_KEY="THE_PATH_TO_THE_PRIVATE_KEY_FILE"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="THE_PASSWORD_FOR_THE_PRIVATE_KEY"
npm run tauri build

3. Set up GitHub

First, choose whether you are going to place the latest.json file in your project repository or in a GitHub Gist.

The latest.json file should look something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// latest.json
{
  "version": "v0.0.1",
  "notes": "Your Release Notes go here",
  "pub_date": "2020-06-22T19:25:57Z",
  "platforms": {
    "darwin-x86_64": {
      "signature": "Content of app.tar.gz.sig",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x86_64.app.tar.gz"
    },
    "darwin-aarch64": {
      "signature": "Content of app.tar.gz.sig",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-aarch64.app.tar.gz"
    },
    "linux-x86_64": {
      "signature": "Content of app.AppImage.tar.gz.sig",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-amd64.AppImage.tar.gz"
    },
    "windows-x86_64": {
      "signature": "Content of app-setup.nsis.sig or app.msi.sig, depending on the chosen format",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x64-setup.nsis.zip"
    }
  }
}
  • version can be with or without the leading v.
  • notes can be any string.
  • pub_date should be in the format YYYY-MM-DDTHH:MM:SSZ.
  • platforms can contain the platforms you are targeting. The keys should be the platform names and the values should be objects with signature and url keys.
  • signature should be the content of the signature file generated when you build for the respective platform.
  • url should be the URL to the binary file from the release. Don’t accidentally put the URL of the release page itself.

Test The Flow!

  1. Set the project version to 0.0.1 in Cargo.toml, tauri.conf.json and package.json.
  2. Set the TAURI_SIGNING_PRIVATE_KEY_PASSWORD and TAURI_SIGNING_PRIVATE_KEY environment variables in your shell.
  3. Build the binaries.
  4. Create a release on GitHub with the binaries.
  5. Update the latest.json file in your GitHub repository or GitHub Gist with the new version, release notes, signature and download URL.

Now, repeat the above steps with a new version, say 0.0.2.

At this point, your GitHub repo will have two releases, and the latest.json file will have the details of the latest release.

Moment of Truth

  1. Download the app from the first release (0.0.1).
  2. Run the app. Click on About and check the version. It should be 0.0.1.
  3. The app will check for updates and show a dialog box if an update is available. If it doesn’t, click on the “Check for Updates” button.
  4. Click the Update button. The app will download the update in the background and restart when done.
  5. The app will now be updated to the latest version (0.0.2). Verify by clicking on About.

Congratulations! You have successfully set up the auto-updater for your Tauri v2 app.

Appendix: Automate the Process

You can automate the process of building the binaries and updating the latest.json file using GitHub Actions. This way, you don’t have to manually build the binaries and update the latest.json file every time you release a new version.

I haven’t yet set it up for myself. When I do, I’ll update this post with the steps.

Questions?

If you have any questions related to Tauri, or need help with setting up the auto-updater for your Tauri v2 app, get in on the Tauri Discord server and ask away. The community is very helpful and responsive.