Guides9 min read

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 secrets keeps it out of logs. Never echo it.
  • Pin native compatibility. When a PR changes native code, bump runtimeVersion in 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.

Related docs