Automate Capacitor OTA releases with GitHub Actions
Turn every merge to main into a live update: a copy-paste GitHub Actions workflow that builds your web app, uploads the bundle, and releases it to a channel with the OtaKit CLI — no manual steps.
Manual releases are where OTA loses its speed advantage. If shipping an update means someone remembering to build, run the CLI, and pick the right channel, releases get batched, delayed, and occasionally done wrong. The fix is to make a merge to your main branch be the release. This guide sets that up with GitHub Actions and OtaKit — copy-paste and adapt.
Goal: push to main, CI builds the web app, uploads the bundle, and releases it to a channel. No laptop, no manual steps, fully auditable.
1. Create a release token
CI authenticates with a token instead of an interactive otakit login. Create one for your app, then add it to your repository as an encrypted secret under Settings → Secrets and variables → Actions — for example OTAKIT_TOKEN. Scope it to just this app; never paste it into the workflow file directly.
2. Add the workflow
This workflow builds the web app and releases the bundle on every push to main. Adjust the build command and web directory to your framework (out for Next.js static export, dist for Vite):
# .github/workflows/release.yml
name: Live update
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- name: Release OTA bundle
run: |
npm install -g @otakit/cli
otakit upload --release production
env:
OTAKIT_TOKEN: ${{ secrets.OTAKIT_TOKEN }}The CLI reads OTAKIT_TOKEN from the environment, so there's no interactive login step. That's the whole pipeline for a straight-to-production flow.
3. Stage before production (recommended)
Straight-to-production is fine for internal tools. For anything user-facing, release to a pre-production channel automatically and promote by hand once it looks healthy. Point the automated release at beta:
- name: Release to beta
run: |
npm install -g @otakit/cli
otakit upload --release beta
env:
OTAKIT_TOKEN: ${{ secrets.OTAKIT_TOKEN }}Then promote the exact bundle you validated — no rebuild — when you're ready. See staged rollouts for the full pattern:
otakit release <bundle-id> --channel production
4. Promote on a tag instead
Prefer promotion to be an explicit, human action? Trigger the production release on a version tag rather than every merge:
on:
push:
tags: ["v*"]Now day-to-day merges flow to beta, and cutting a v1.4.2 tag ships production. Your release history lines up with your git tags.
Keep it secure and correct
- Mask the token. Passing it via
secretskeeps it out of logs. Neverechoit. - Pin native compatibility. When a PR changes native code, bump
runtimeVersionin the same change so old shells don't receive an incompatible bundle. See OTA security. - Fail loudly. Let the job fail if the build fails — a broken build should never reach a release step.
OtaKit doesn't bill by monthly active users or bandwidth, so wiring this into CI won't surprise you with a metered bill as your rollout widens.
Where to go next
The CI automation docs cover tokens and flags in detail. Pair this with staged rollouts and the common mistakes checklist for a release process you can trust.