---
title: "Caching React Native Builds on S3 and R2 With Rock"
description: "Learn how to cache your React Native builds and store them on Amazon S3 or R2 to reuse across your team."
url: https://www.callstack.com/blog/caching-react-native-builds-on-s3-and-r2
updated: 2026-10-24T08:39:28.669Z
---

# Caching React Native Builds on S3 and R2 With Rock

Learn how to cache your React Native builds and store them on Amazon S3 or R2 to reuse across your team.

_By Michał Pierzchala, Principal Engineer @ Callstack · Published 2025-06-19 · Category: CI/CD_

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/fingerprint`library, that allows us to calculate a unique hashes that represent iOS and Android project. When you pair that with our [GitHub Action](https://github.com/callstackincubator/ios), which will handle building, uploading and downloading your native builds from GitHub Artifacts.

![](https://www.callstack.com/assets/6853f066716df2d9fff0fa3e_image.png)

_Example of [Expensify app](https://github.com/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](https://twitter.com/rnef%5Fdev?ref%5Fsrc=twsrc%5Etfw) 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](https://t.co/sNVapTpFyL)
> > 
> > — Michał Pierzchała (@thymikee) [May 23, 2025](https://twitter.com/thymikee/status/1925944454835327175?ref%5Fsrc=twsrc%5Etfw)

[Post on X](https://twitter.com/thymikee/status/1925944454835327175)

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:

```javascript
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:

```javascript
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](https://github.com/callstack/rock/pull/263) 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:

```diff
-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:

```yaml
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](https://www.rockjs.dev/docs/getting-started/migrating-from-community-cli) or [start a new project](https://www.rockjs.dev/docs/getting-started/).

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](https://www.rockjs.dev/docs/configuration#custom-remote-cache-provider).

## 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!
