A Lightweight CI/CD Workflow for Expo Apps Using EAS & GitHub Actions
Jun '25When you’re juggling multiple Expo apps — maybe one for a logistics tool, another for a small POS, and a few side experiments — the last thing you want is to spend hours building, uploading, or manually testing each change. I went through that pain for months: local builds breaking, mismatched environments, manual OTA updates, and “who built this version again?” moments.
Eventually, I decided to build a light, automated CI/CD workflow that would take care of everything: testing, previewing, and deploying — while staying fast and simple enough for small teams or solo developers.
Here’s how I did it.
Why Even Small Apps Deserve CI/CD
When I was rebuilding Kamioun’s mobile apps, every push meant a new release candidate to test. Doing that manually with Expo was fine for one app, but once I started building other projects — POS Light, Spearspots, internal tools — it quickly became unmanageable.
CI/CD became not about “being fancy,” but about speed and consistency.
I wanted:
- Automatic preview builds for every PR.
- One-click deployments to production via
main. - OTA updates pushed through EAS Update with proper channels.
- All builds and versions tracked without opening my laptop.
The Stack
- Expo EAS for builds, submissions, and OTA updates.
- GitHub Actions for CI/CD orchestration.
- Expo tokens & secrets stored in GitHub repo secrets.
- EAS channels & branches to separate environments (
dev,staging,production). - (Optional) Slack notifications for build results.
The Core Workflow
When I push to main, GitHub Actions automatically:
- Runs lint, type checks, and lightweight tests.
- Builds for iOS and Android using EAS Build.
- Submits to stores (or to internal testers).
- Creates a new OTA update via EAS Update.
Here’s the minimal workflow file:
name: Build & Deploy Expo App
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- run: yarn install --frozen-lockfile
- run: yarn lint && yarn tsc --noEmit
- run: eas build --platform all --profile production --non-interactive
- run: eas submit --platform all --non-interactive
- run: eas update --channel production --non-interactiveThat single file means no more manual builds — just push to main and watch EAS do the rest.
Preview Builds for Every Pull Request
This part changed everything.
Every PR now triggers a preview build on EAS with its own channel. It lets me or a teammate instantly test a branch before merging.
name: PR Preview Build
on:
pull_request:
branches:
- main
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- run: yarn install --frozen-lockfile
- run: eas build --profile preview --platform all --non-interactive
- run: eas update --channel preview-${{ github.head_ref }} --non-interactiveThen, the Action posts a comment in the PR:
✅ Preview ready!
[iOS link](#) • [Android link](#)
This meant every contributor could open the app instantly in Expo Go, test the feature live, and merge with confidence.
Lightweight Testing
Before building, each run performs:
yarn lint yarn tsc --noEmit
It’s minimal, but it catches most issues — broken imports, unused vars, or failing builds — early. For more complex apps, I add Detox end-to-end tests only for critical flows.
OTA Updates: Fix Bugs in Minutes
For solo projects, EAS Update is a game-changer.
If I find a small bug post-deploy, I just push a patch to the production channel:
eas update --branch main --channel production
No rebuild, no resubmit — it’s live for all users in under 60 seconds. This workflow has saved me countless hours, especially during app store reviews.
The Result
I now ship faster than ever:
- PR → preview build in ~5 minutes.
- Main push → production build & OTA in ~10 minutes.
- No manual steps.
For small teams or indie devs working across multiple apps, this setup strikes the perfect balance: automated, transparent, and light enough to maintain.
In the end, CI/CD isn’t about having fancy pipelines — it’s about not thinking about pipelines at all. When everything runs smoothly, you’re free to focus on what matters: building great mobile experiences.