Hot Updater: a self-hosted OTA update server for React Native
A self-hostable OTA update solution for React Native (Alternative to CodePush)
At a glance
- What is it?
- Hot Updater replaces CodePush-style managed updates with a plugin-configured server you run yourself, using Supabase, Cloudflare, AWS S3 or Firebase as storage. It is at v1.0.0-rc.14, and the release candidate status is the first thing to weigh.
- Who is it for?
- Adopt Hot Updater if you already run Supabase, Cloudflare, S3 or Firebase and want update metadata in infrastructure you control, and if you can tolerate a release candidate: the newest line is v1.0.0-rc.14 while v0.36.12 is the stable-looking tag. Do not adopt it if you need a vendor SLA or a managed dashboard, because nothing in the README provides one.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Hot Updater replaces, and who ends up running it
React Native apps can ship JavaScript and Hermes bytecode changes without going through App Store or Play Store review. The managed way to do that has been CodePush, which the repository names directly in its description as the alternative it targets. Hot Updater takes the same job and moves the server side into infrastructure you already pay for.
The README states the goal plainly: "Self-Hosted: Complete control over your update infrastructure." That sentence defines the audience. It is for teams that already operate a Supabase project, a Cloudflare account, an AWS account or a Firebase project, and who would rather add an update table and a storage bucket there than send bundle metadata to a third party. It is also for teams that need iOS and Android from one pipeline, since the README lists multi-platform support as a feature rather than a separate product.
It is not for someone who wants to click a dashboard and be done. There is a web console, and the docs cover a "hot updater console" workflow, but the console is part of a stack you configure and host. The repository is a pnpm workspace with packages/, plugins/, examples/, docs/ and e2e/ directories, so the shape of the project is a set of libraries plus a server, not a single binary you drop on a VPS.
The plugin split: build, storage, database
Configuration is a defineConfig call with three cooperating parts. A build plugin produces the bundle, and the README shows bare({ enableHermes: true }) in every example. A storage plugin holds the bundle artifacts. A database plugin holds the metadata that tells a client which bundle is current.
That split is the interesting design decision. Storage and database are separate plugins even when they come from the same vendor, so the Supabase example imports supabaseStorage and supabaseDatabase separately, and the Cloudflare example pairs r2Storage with d1Database. You can therefore put artifacts in R2 and metadata in D1, or artifacts in S3 and metadata in S3 through the AWS plugin's s3Storage and s3Database pair. The trade-off is that you must reason about two systems instead of one, and about what happens when they disagree: if a row points at an object that was deleted, the client has nothing to download.
The fourth key is updateStrategy, set to "appVersion" in all four README examples. The README also mentions "Version Control: Robust app version management through semantic versioning" as a feature. What the README does not do is enumerate the other values updateStrategy accepts, so treat "appVersion" as the documented default and check the docs site before assuming alternatives exist.
Build plugins are not limited to bare. The README lists Metro, Re.Pack and Expo under build plugins, and the examples directory contains expo-52 and rock-repack alongside plain React Native version examples.
Installing Hot Updater and shipping a first bundle
The README does not give a step-by-step install. It points to https://hot-updater.dev for full documentation, and the CLI ships on npm as hot-updater. The configuration file is where the real setup happens, so the first concrete step is creating it with a storage and database pair.
The Supabase configuration below is copied from the README. It reads credentials from a .env.hotupdater file via dotenv, so those four environment variable names must exist before the config will load.
import { bare } from "@hot-updater/bare";
import { supabaseDatabase, supabaseStorage } from "@hot-updater/supabase";
import { config } from "dotenv";
import { defineConfig } from "hot-updater";
config({ path: ".env.hotupdater" });
export default defineConfig({
build: bare({ enableHermes: true }),
storage: supabaseStorage({
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
bucketName: process.env.HOT_UPDATER_SUPABASE_BUCKET_NAME!,
}),
database: supabaseDatabase({
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
supabaseServiceRoleKey: process.env.HOT_UPDATER_SUPABASE_SERVICE_ROLE_KEY!,
}),
updateStrategy: "appVersion",
});If you would rather stay on Cloudflare, the same shape applies with r2Storage and d1Database, using HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME, HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID, HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID and HOT_UPDATER_CLOUDFLARE_API_TOKEN among the variables.
For agents, the README documents an optional skill install:
npx skills add hot-updater/skillsAfter that, prompts such as $hot-updater deploy using the current app version or $hot-updater roll back the most recently deployed bundle are the documented interaction pattern. The README does not print the raw deploy command, so read the AI Agent Guide or the CLI docs before scripting a release.
Bundle diffing and the fallback that keeps it safe
The most consequential feature is incremental delivery. The README says deploys prepare .bsdiff patches for changed Hermes bundles by default, and that a diff-enabled runtime reuses bundle files already on the device. Its own illustration is a release that would otherwise ship a 10 MB archive arriving as roughly a 600 KB patch when the Hermes bytecode change is small.
Treat that number as the README's illustration, not a guarantee. The size of a patch depends on how much bytecode moved, and the README is explicit that the fallback exists: "If a patch is missing, incompatible, or not worth using, Hot Updater falls back to the normal archive update path." That sentence is the one that makes the feature safe to enable, because a client that cannot apply a patch still gets a working update.
The cost is operational. You now have two artifact kinds to reason about, full archives and patches, and a runtime decision about which to use. The README points to a Bundle Diffing guide for "the full runtime behavior and fallback rules", which is where the conditions for rejecting a patch live. If you plan to enable diffing, that page is the one to read before your first production release, not the feature list.
Where Hot Updater is the wrong tool
The release status is the first limitation. The most recent tag is v1.0.0-rc.14, dated 2026-09-10, and the v1.0.0 line is a release candidate. A team that needs a frozen API for a long-lived app should weigh that against v0.36.12, which was published the same day on the older line. Running two lines in parallel is normal for a project approaching 1.0, but it means you must decide which one you are tracking and read the changelog before upgrading.
The second limitation is that self-hosting is the product. There is no managed tier described in the README, so the operational burden of the database and bucket is yours. If nobody on the team owns that infrastructure, a managed service will cost less in engineering time than Hot Updater saves.
The third is the plugin surface itself. Four vendor examples are documented, and each carries its own credential set and its own failure modes. A misconfigured bucket name or a service role key with the wrong scope produces a deploy that succeeds locally and fails on device. Nothing in the README describes a dry-run mode or a preflight check, so the verification loop is your own.
Finally, the licence. The repository's package.json declares "license": "MIT", while the repository metadata reports NOASSERTION. Those two disagree, and only the LICENSE file in the repository resolves it. Check that file before you build a compliance argument on either signal.
How it compares to CodePush and to Expo's update service
The description frames Hot Updater as an alternative to CodePush, and the difference is where the control plane lives. CodePush, as a managed service, owns the update endpoint and the release history; you call an API and it decides what devices receive. Hot Updater inverts that. Your storage plugin holds the artifacts, your database plugin holds the metadata, and the client asks an endpoint backed by your own account. The practical consequence is that revocation, retention and access control are your configuration rather than a vendor setting.
Expo's own update service is the other comparison worth naming, and it is a different axis. It is managed and tied to the Expo toolchain. Hot Updater lists Expo as a build plugin, and the repository ships examples/expo-52, so it can serve an Expo project while keeping storage and metadata in your accounts. The choice is not Expo versus bare React Native; it is managed hosting versus infrastructure you operate, with both able to build an Expo app.
A third option is simply staying on the older v0.36.x line if you are already running it. That is not an alternative product, but it is a real decision point given that v1.0.0 is still a release candidate.
Maintenance, upgrades and what the repository tells you
The last push was on 2026-09-10, and the repository is not archived. Two releases landed that day, v1.0.0-rc.14 and v0.36.12, which indicates the maintainers are still publishing on both lines. That is the only maintenance signal available here, and it is a snapshot rather than a commitment.
The upgrade cost is concentrated in two places. The first is the CLI and the runtime package, which are versioned together and whose release notes are the place to look for breaking changes between the v0.36 line and the v1.0.0 candidates. The second is the plugin configuration, because a plugin package version and the server version can drift. The repository uses Changesets, visible as the .changeset directory, so per-package version bumps are tracked rather than released as one monolith. That helps, but it also means an upgrade can touch several packages at once.
On licence, the MIT declaration in package.json is permissive and would normally mean you can use the code commercially with attribution requirements defined by the licence text. The NOASSERTION value in the repository metadata is a tooling signal, not a legal position, and it may simply mean the licence could not be matched automatically. Read LICENSE yourself; this is not legal advice.
Editorial conclusion
Adopt Hot Updater if you already run Supabase, Cloudflare, S3 or Firebase and want update metadata in infrastructure you control, and if you can tolerate a release candidate: the newest line is v1.0.0-rc.14 while v0.36.12 is the stable-looking tag. Do not adopt it if you need a vendor SLA or a managed dashboard, because nothing in the README provides one. Before committing, verify two things in your own environment: that your chosen plugin pair (for example r2Storage with d1Database) actually satisfies the storage and database roles your update flow needs, and that the bundle diffing fallback behaves as the Bundle Diffing guide describes when a patch is rejected.
Frequently asked questions
What is Hot Updater for React Native?
It is a self-hostable OTA update solution for React Native, described in the repository as an alternative to CodePush. You configure a build plugin, a storage plugin and a database plugin, and the update artifacts and metadata live in infrastructure you control.
How do I install Hot Updater in an Expo project?
The README points to https://hot-updater.dev for full documentation and does not print install steps itself. It lists Expo as a supported build plugin and the repository includes an examples/expo-52 directory, so the Expo path is documented on the docs site and in that example.
Which storage and database backends does Hot Updater support?
The README gives configuration examples for Supabase (supabaseStorage and supabaseDatabase), Cloudflare (r2Storage and d1Database), AWS S3 plus Lambda@Edge (s3Storage and s3Database) and Firebase (firebaseStorage and firebaseDatabase). Build plugins listed include Metro, Re.Pack and Expo.
Does Hot Updater support incremental OTA downloads?
Yes. The README states that deploys prepare .bsdiff patches for changed Hermes bundles by default, and that a diff-enabled runtime reuses bundle files already on the device. If a patch is missing, incompatible or not worth using, the README says Hot Updater falls back to the normal archive update path.
Is Hot Updater stable enough for production?
The newest release listed is v1.0.0-rc.14, a release candidate, while v0.36.12 was published the same day on the older line. The README does not state a stability guarantee for either, so the decision depends on whether you can track a release candidate and read the changelog before upgrading.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/gronxb-hot-updater)