Guides8 min read

Semantic versioning for OTA bundles and native runtimes

How to version OTA bundles and keep them compatible with the native shells in the wild. A practical semver strategy for Capacitor live updates using runtime versions and channels.

Versioning gets confusing with OTA because there are really two versions in play: the native app version that goes through the store, and the web bundle version that ships over the air. Keep them straight and releases stay predictable. Blur them and you get bundles reaching shells that can't run them. This guide lays out a simple, durable scheme for OtaKit.

Mental model: the store version answers “which native shell is this?” The bundle version answers “which web build is this?” A third value — runtime version — connects them.

The three numbers that matter

ValueWhat it identifiesChanges when
App version (store)The native binary users installedYou submit to the App Store / Play
Bundle versionA specific web buildEvery OTA release
Runtime versionWhich shells a bundle is compatible withNative code changes

Version bundles with semver

Treat each OTA bundle like a package release. A pragmatic reading of semver for web bundles:

  • Patch (1.4.1) — bug fixes, copy, styling. Safe, frequent, background updates.
  • Minor (1.5.0) — new features that don't need new native capabilities. Still OTA-able.
  • Major (2.0.0) — usually paired with a native change, which means a store release and a runtime version bump.

Runtime version is the compatibility contract

This is the one that prevents crashes. When a store build changes native code — a new plugin, a permission, a Capacitor upgrade — bump runtimeVersion. Bundles are matched to compatible shells, so a bundle built for the new native surface never lands on an old install that can't run it.

// capacitor.config.ts
const config: CapacitorConfig = {
  appId: "com.example.myapp",
  appName: "My App",
  webDir: "dist",
  plugins: {
    OtaKit: {
      appId: "YOUR_OTAKIT_APP_ID",
      runtimeVersion: "2", // bump when native code changes
    },
  },
};

OtaKit also checks your bundle's dependencies at upload time and warns when it detects native code the target shells don't have — a safety net on top of the version you set.

A workable convention

  1. Keep the native app version and runtime version in lockstep for store releases.
  2. Increment bundle patch/minor freely for OTA releases against the same runtime.
  3. When you cut a store release with native changes, bump runtime version and start the new bundle line there.

If a change forces a runtimeVersion bump, it's a store release — not an OTA update. That single rule keeps your versioning honest.

Where to go next

See channels & runtime version for the API, and common OTA mistakes for the versioning traps to avoid.

Related docs