Migrate from Firebase App Distribution
Firebase App Distribution (FAD) ships builds to QA inside the Firebase ecosystem. It works, but the surface has not meaningfully evolved in years and a few rough edges have become routine pain:
- Testers need to onboard through the Firebase App Tester app on Android, or accept an invitation tied to a Google account.
- No branch or environment organisation. All builds for an app live in a single chronological list.
- Tightly coupled to Firebase. If you do not otherwise use Firebase Analytics, Crashlytics, or Auth, you are pulling in a SDK and a Google Cloud project just for distribution.
- Limited release-tagging story; "Release" in FAD is a build, not a version label you can pin a URL to.
- The web dashboard does not surface QR codes; testers click through email or the Tester app.
buildtree replaces the distribution piece directly. Builds keep being built the way you build them; only the upload step changes. This guide assumes you have a FAD project with one or more apps and a CI pipeline that calls the FAD CLI, Fastlane plugin, or Gradle plugin.
What you keep, what you replace
| FAD feature | buildtree equivalent |
|---|---|
| App (one per platform) | Project (cross-platform; iOS + Android live in the same project) |
| Tester groups | Allowlist + Private mode (see Install access) |
| Public link via Firebase Hosting | Public install URL on every build |
firebase appdistribution:distribute | buildtree upload |
firebase_app_distribution Fastlane action | buildtree upload |
Gradle appDistributionUpload task | buildtree upload |
| Release notes | Build metadata (auto-extracted from IPA/APK) + release tags |
| Group / tester management | Allowlist editor in the project dashboard |
| Build categories (none) | Branch + environment + release tag |
| iOS UDID via Apple Dev portal manually | Built-in UDID registration flow (see iOS distribution) |
The 5-minute migration
-
Install the CLI and authenticate:
npm install -g @buildtree/cli buildtree login -
Replace your FAD upload step. In your CI workflow, swap the FAD invocation for:
buildtree upload ./app.apk --env prodThe first time you run this interactively the CLI prompts you to create a project and pick a slug; that slug becomes part of your install URLs and is reused on every subsequent upload. For preview builds tied to a feature branch:
buildtree upload ./app.apk --env dev --branch feature/paymentsThat is the only line that changes. Your build commands stay the same.
-
Share the install URL with your QA team. The dashboard shows three URL types:
- Pinned: a specific build, frozen forever.
- Folder: always-latest for a given
(env, branch)pair. - Release: frozen to a release tag.
Most teams share the folder URL so testers always grab the freshest build without anyone copy-pasting a new link each release.
CI integration
Firebase CLI
If your CI runs firebase appdistribution:distribute today:
firebase appdistribution:distribute ./app.apk \
--app 1:1234567890:android:abcdef \
--groups internal-testers \
--release-notes "Build $GITHUB_SHA"
Replace with:
buildtree upload ./app.apk --env prod
No app ID needed (the binary's bundle ID is read from the file). No --groups flag (testers are controlled by the project's allowlist on the dashboard, which doesn't change per build). Release notes map to a release tag with --release v1.5.0 if you want a frozen install URL per version.
Fastlane
Replace the firebase_app_distribution action with a shell call:
lane :distribute do
sh "buildtree upload ../app.ipa --env prod"
end
Pass the API token via the BUILDTREE_TOKEN environment variable on the runner.
Gradle
Drop the com.google.firebase.appdistribution plugin and its config block. Add a single Gradle task or invoke the CLI directly in your CI step after assembleRelease:
./gradlew assembleRelease
buildtree upload ./app/build/outputs/apk/release/app-release.apk --env prod
GitHub Actions
Generate an API token from the dashboard's Settings → API Tokens page and add it as a GitHub repository secret called BUILDTREE_TOKEN. Then:
- name: Distribute to buildtree
env:
BUILDTREE_TOKEN: ${{ secrets.BUILDTREE_TOKEN }}
run: |
npm install -g @buildtree/cli
buildtree upload ./build/app.apk --env prod
Full example workflows for iOS and Android: see GitHub Actions.
Tester onboarding
FAD requires testers to either install the Firebase App Tester app (Android) or accept an invitation tied to a Google account. buildtree replaces this with a one-line shareable URL:
- Public projects: anyone with the URL can install. No tester signup, no Google account, no email gate.
- Private projects (Solo and above): testers verify their email once via a magic link. They never sign up for an account or download anything. See Install access.
For most QA workflows this is the largest day-one win over FAD. External testers, contractors, and clients no longer need to be added to a Google project.
iOS UDID handling
FAD documents asking testers to register their UDID separately, then you paste it into the Apple Developer portal manually before re-uploading the build. buildtree builds the UDID flow into the platform:
- Share the project's public registration URL with iOS testers.
- They open it on the device they want to install on, fill in name and email.
- iOS prompts them to install a profile; the device's UDID lands in your dashboard.
- You approve each request from the Devices page.
- One click exports approved UDIDs in Apple Dev Portal format (the tab-separated
UDID<TAB>Nameformat the bulk-add tool expects). - Re-build your IPA with the new provisioning profile and upload.
The whole loop usually happens inside an hour for a tester you have never met. See iOS distribution for the full flow.
Release notes
FAD stores a freeform --release-notes string per build. buildtree extracts version, build number, bundle ID, and display name from the IPA or APK at upload time, so the "what changed" facts are surfaced without you typing them.
For human-readable changelogs, the closest equivalent is a release tag:
buildtree upload ./app.ipa --env prod --release v1.5.0
The release URL is frozen and shareable. Pair it with the changelog entry in your repo, in Slack, or in the PR description. A native release-notes field is on the roadmap.
What does not transfer automatically
- Tester groups: there is no programmatic import of FAD tester rosters today. Export the email list from the FAD console and paste it into the buildtree allowlist as a multiline block (one email per line, or
*@yourcompany.comfor whole-team coverage). - Historical builds: FAD storage stays where it is until you delete the Firebase project. If you want to preserve specific release URLs on buildtree, re-upload the IPA or APK with
buildtree upload --release <tag>. - Crashlytics / Analytics: these are separate Firebase products. They keep working with FAD removed; nothing in your app code changes. buildtree does not replace them.
Common questions
Do I need to change my build pipeline? No. buildtree replaces the upload step only. Local Xcode builds, Fastlane lanes, EAS local builds, native Gradle, and CI builds on any provider all produce a file, and buildtree upload takes it from there.
Can I keep Crashlytics? Yes. Crashlytics and FAD are unrelated products under the Firebase umbrella. Removing the FAD plugin or Fastlane action does not affect the Crashlytics SDK in your app.
Is the QA install link the same URL each release? Yes if you share the folder URL (/install/folder/<project>/<env> or /install/folder/<project>/<env>/<branch>). It always points to the latest build for that environment. Bookmark once, get fresh builds forever.
What does it cost compared to FAD? FAD is free as long as Google keeps the lights on (no SLA on continued availability). buildtree's Free tier covers 1 project, 5 GB, 30-day retention.
Need help
If you are migrating a larger team and want help mapping FAD tester groups to buildtree allowlists, email hello@buildtree.sh.