Caching React Native Builds on S3 and R2 With Rock

One of the reasons we’ve decided to build Rock was the ability reuse native iOS and Android build artifacts by completely skipping builds that don’t need to happen. In our experience up to 95% of the commits for large-scale React Native apps only affect the JavaScript side. And yet, most teams will try to build the entire native app from scratch for every commit.

If that’s you, you’re spending up to 20x more compute on native builds than necessary. That’s a lot of wasted time (and money)!

Unless you use Rock In our framework, we’ve implemented a fingerprinting mechanism, based on @expo/fingerprintlibrary, that allows us to calculate a unique hashes that represent iOS and Android project. When you pair that with our GitHub Action, which will handle building, uploading and downloading your native builds from GitHub Artifacts.

Example of Expensify app saving time and $$$ on dev builds only.

The remote cache experience

With all that you can achieve a pretty great DX. Assuming your builds are happening in GitHub cloud, you have installed npm deps, and have the GitHub Personal Access Token set up, but haven’t build the project yet:

  1. Start the dev server with rock start
  2. Run the rock run:ios (or rock run:android) command in your Terminal, which will calculate the native hash for your project
  3. The command will try to download the binary from GitHub Artifacts
    1. If it succeeds, the matching native artifact (APK, APP, or IPA) will be downloaded and installed to your selected device
    2. If it doesn’t find the artifact on remote cache, it will proceed with building locally, as you’re probably used to.

If all goes well, you’ll be up and running with your app from seconds. And if you have your GitHub Action set up to work on branches as well, you’ll be able to quickly jump into your colleague’s work in seconds too, even if it involves native changes.

Store builds on S3, R2 or anywhere

Storing the artifacts on GitHub was a major limitation for us. That’s why I’m particularly excited about the new feature that we shipped in latest Rock version: support for AWS S3 and Cloudflare R2 storage providers.

Finally @rnef_dev is off the GitHub artifacts leash. Next week we're shipping S3 remote cache provider 🫡

You can store your APK, AAB, APP, and IPA binaries on your favorite storage, seamlessly integrated with RNEF CLI pic.twitter.com/sNVapTpFyL

— Michał Pierzchała (@thymikee) May 23, 2025

All you need to do is to add @rock-js/provider-s3 to your configuration and populate it with necessary data describing your bucket (its name and region), and also the access keys necessary to interact with AWS S3 API:

import { providerS3 } from '@rock-js/provider-s3';

export default {
  // ...
  remoteCacheProvider: providerS3({
    bucket: 'your-bucket',
    region: 'your-region',
    accessKeyId: process.env.S3_ACCESS_KEY_ID,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  }),
};

‍
And if instead of S3 you want to use R2, all you need to do is add the endpoint config option, which points to your bucket on Cloudflare:

remoteCacheProvider: providerS3({
  endpoint: 'https://${ACCOUNT_ID}.r2.cloudflarestorage.com',
  // ... rest of the config
}),

Ok, that’s almost all you need to do. Adding this feature required as to create better internal abstractions for our GitHub Actions, for which we’ve created a new remote-cache command to handle all providers in a universal way. As a result, we’ve released a v2 of our callstackincubator/ios action, currently tagged as v2-rc, that handles building, downloading, uploading, and deleting iOS artifacts from S3 or R2 for you.

If you’re already using Rock, you can update your workflow file like this:

-uses: callstackincubator/ios@v1
+uses: callstackincubator/ios@v2-rc

And add pass necessary environment variables (unless you have other means of populating these) to that action:

env:
    S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
    S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}

And if you’re not using Rock yet, follow the migration from Community CLI docs or start a new project.

Going forward, the v2 of our GitHub Actions for both iOS and Android will support any remote cache provider compatible with Rock, be it GitHub, S3, R2, or a custom one which you can build yourself.

Give it a try

We’re super excited about the new capabilities of Rock and that you’ll now be able to use it in a more broad range of scenarios. Please give it a go, test it with your buckets and leave us feedback in a form of GitHub issues and PRs. We’re off the GitHub Artifacts leash for good and on for some new features to come. Stay tuned!

Insights

Learn more about CI/CD

cover

Aug 21Article

Automating React Native Evals With Airflow

How Airflow coordinates React Native Evals, runs solver and judge models separately, and keeps every result traceable through human review.

cover

Oct 29Podcast

How to Handle App Center Retirement

Discover App Center alternatives for React Native. Explore migration tips and Expo's role in the transition from CodePush & other App Center functionalities.

cover

Aug 4Article

Mastering Fastlane: Overcoming Challenges in Passing Build Properties to Native Layers

Master passing build properties with Fastlane for efficient application development.

cover

May 14Article

Ship OTA (Over-The-Air) When in an Emergency

Shipping Over-The-Air may save your app from a lot of trouble. Find out why and how you can make your application OTA-ready.

cover

May 10Article

Shipping Fast With Continuous Deployment

Continuous Deployment (CD) lets you ship new features or check for critical bugs fast. Find out more about developing apps with a CD setup.

cover

Dec 13Article

How Can Continuous Integration (CI) Improve Your React Native Apps?

Read the article and find out how Continuous Integration (CI) can improve the building, testing, and distribution of your React Native apps.

cover

Apr 24Article

Continuous Delivery - Integration for Everyone

A quick guide on how to tame Continuous Delivery and Integration. Dive into a real-world scenario from a Callstack client project.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.