How to setup Automatic updates for Electron apps using Forge and Cloudflare

I am using Electron Forge to build my Electron App Mindsaha. Here's how I setup an automatic updates server for my desktop app, so that whenever I release a new version, users will be notified and updates will be installed automatically.

Tech stack

This How-to assumes that:

  • You have an Electron application setup already
  • You are using Electron Forge to package your electron application
  • You want to setup update server using cloud object storage.

1. Setup Cloudflare storage

Cloudflare provides an object storage service called R2 allows developers to store large amounts of unstructured data. The good thing is that there's no charge for data transfer to internet, so you won't need to pay anything if users download your application.

  1. Signup to Cloudflare and Create a R2 storage bucket names 'yourapp' (example only, you can use any name).
  2. Expose the bucket's content to internet via either custom domain (like get.yourapp.com) or a cloudflare managed sub-domain. I'd recommend use custom domains.
  3. Create a folder called updates under your bucket. We will use this updates folder to store our electron update files.

2. Preparing release meta data

Now that we have our server setup, next step is to prepare the release files and meta data to store in the server. For mac updates, we need RELEASES.json file and for Windows we need RELEASES file. When user launches your electron app, the updater code will fetch these two files based on the platform and determine if there's a new version available or not.

Creating Mac RELEASES.json file

When you run npm run make, on macOs, there won't be any RELEASES.json file be created. So, I came up with a small script that creates the JSON file for me. I created this file under the PROJECT_ROOT/scripts/releaseJson.js path.

// PROJECT_ROOT/scripts/releaseJson.js

var pjson = require("../package.json");
var path = require("node:path");
var fs = require("node:fs");

// run only in mac
if (process.platform !== "darwin") return;

const { platform, arch } = process;

const releaseVersion = pjson.version;
const outPath = path.resolve("out");
const releaseJsonPath = path.join(
  outPath,
  "make",
  "zip",
  platform,
  arch,
  "RELEASES.json",
);

const releaseMeta = {
  currentRelease: releaseVersion,
  releases: [
    {
      version: releaseVersion,
      updateTo: {
        version: releaseVersion,
        pub_date: new Date().toISOString(),
        notes: "Features and bug fixes",
        name: releaseVersion,
        url: `https://get.yourapp.com/updates/${platform}/${arch}/yourapp-${platform}-${arch}-${releaseVersion}.zip`,
      },
    },
  ],
};

fs.writeFileSync(releaseJsonPath, JSON.stringify(releaseMeta), { flag: "w+" });

Take a note at the url property - here you should correctly mention the public URL of your cloudflare R2 bucket and the updates folder you created in the previous step. Next update the package.json to call this script during make step "make": "npm run clean && electron-forge make && node scripts/releaseJson.js". Here's a sample output.

{
  "currentRelease": "1.18.0",
  "releases": [
    {
      "version": "1.18.0",
      "updateTo": {
        "version": "1.18.0",
        "pub_date": "2025-05-17T05:38:11.951Z",
        "notes": "Features and bug fixes",
        "name": "1.18.0",
        "url": "https://get.yourapp.com/updates/darwin/arm64/yourapp-darwin-arm64-1.18.0.zip"
      }
    }
  ]
}

Creating Windows RELEASES file

For Windows, we do not need any extra script, as the make itself creates the RELEASES file properly. Here's the sample file for Windows

1B0863D5C31C795C2EADA94DE21D994D1A810AAC yourapp-1.18.0-full.nupkg 119179484

So finally our release files structure should look like this.

yourapp
└── out
    └── make
        ├── zip
        │   └── darwin
        │       └── arm64
        │           ├── yourapp-darwin-arm64-1.18.0.zip
        │           └── RELEASES.json
        └── squirrel.windows
            └── win32
                └── x64
                    ├── yourapp-1.18.0 Setup.exe
                    ├── yourapp-1.18.0-full.nupkg
                    └── RELEASES

3. Uploading release artifacts to Cloudflare bucket

Once you have built the release files, you need to transfer the generated files to the Cluodflare R2 bucket. You can either do this step manually or automate this via script. For my purpose, I have been transferring files manually so far. May be in near future, I will update this section once I automate the file copy to R2 bucket.

4. Configuring Electron app to look for updates

Now that you have your release server ready, next step is to instruct your electron app to look in this server for any updates. You do this by placing this piece of code anywhere in your main.js file.

const { updateElectronApp, UpdateSourceType } = require('update-electron-app');
const logger = require('electron-log');

// other code
updateElectronApp({
  updateSource: {
    type: UpdateSourceType.StaticStorage,
    baseUrl: `https://get.yourapp.com/updates/${process.platform}/${process.arch}`
  },
  logger
});

Ensure that the baseUrl property has the corrent public URL of your Cloudflare R2 bucket and the directory structure matches the one you have in your bucket.

With this setup, whenever you upload a new version of your app to Cloudflare bucket, user's will be notified about an when they open the app and update will be automatically downloaded and installed.