Simon Boisset
Back to blog

Expo CI/CD workflows: automated builds, fingerprinting, and OTA

January 12, 2026

Expo CI/CD workflows: automated builds, fingerprinting, and OTA

Expo CI/CD workflows: automated builds, fingerprinting, and OTA

In an Expo/React Native project, the question isn’t “how do I build?” but “when do I truly need to build?”. Native builds take time (and cost CI minutes), while OTA updates are almost instant… but they don’t apply to every kind of change.

The goal of this CI/CD workflow is to automate the full chain (native builds, OTA updates, submissions) while avoiding unnecessary builds, using Expo fingerprinting and explicit versioning.


The setup: 2 branches, 2 environments

I structure the flow around two branches:

  • staging (or dev): validation and QA
  • main: production

The principle is the same for both:

  1. compute a fingerprint
  2. decide between a native build or an OTA update
  3. publish automatically

The difference: in production, we add store submission when a native build happened.


Fingerprinting: automatically deciding between build and OTA

The fingerprint is a hash computed from what truly impacts the native binary:

  • native dependencies and modules
  • Expo config and plugins
  • certain config files
  • the marketing version (if you inject it into the config)

Simple rule

  • Fingerprint matches an existing build → OTA update is enough
  • Fingerprint differs → a new native build is required

Result: you rebuild only when a native change actually requires it.


Versioning: explicit marketing version, auto-incremented build numbers

I intentionally separate:

  • marketing version (readable, intentional): controlled by us
  • build numbers (buildNumber on iOS / versionCode on Android): left to Expo/EAS (auto-increment)

The marketing version lives in package.json:

{
  "name": "my-app",
  "version": "1.4.0"
}

app.config.ts: importing package.json to set the marketing version

import type { ConfigContext, ExpoConfig } from "expo/config";
import pkg from "./package.json";

export default ({ config }: ConfigContext): ExpoConfig => ({
  ...config,
  version: pkg.version,
});

Conclusion & how I can help

Setting up this kind of Expo CI/CD workflow can significantly reduce build costs, speed up release cycles, and make production releases safer thanks to clear, automated rules.

Every team and product has its own constraints (release process, store requirements, existing codebase), so the best results usually come from a workflow that’s tailored to your context.

👉 Want to implement or improve an Expo CI/CD pipeline (fingerprinting, OTA channels, versioning strategy, automated submissions) for your app?

I can help you design the architecture, configure EAS, wire up CI, and apply Expo/React Native best practices end-to-end.

➡️ Book a call with me and we’ll build a workflow that fits your product and your team.

Simon Boisset

I'm Simon Boisset, a mobile/full-stack developer. I help teams ship React Native/Expo apps, clean up legacy stacks, and make releases predictable.

Book a call