Skip to main content
Remote builds are an enterprise-only feature. Contact [email protected] or book a call to enable them for your organization.
Remote builds compile your mobile source code on dedicated Revyl build runners. iOS builds run on dedicated Mac runners; Android builds run on dedicated Ubuntu runners with the Android SDK, Gradle, JDK, Node, and common package managers preinstalled. Device and simulator sessions stay separate from build runners.

Who This Is For

Use remote builds when your team needs dedicated mobile build capacity without managing runner infrastructure. This is especially useful when:
  • Your CI provider cannot supply enough mobile build capacity.
  • Your builds need pinned Xcode/macOS or Android SDK/JDK/Gradle environments.
  • You want Revyl to produce the simulator .app or emulator-installable .apk and immediately register it for testing.
  • Your build depends on private package mirrors or artifact stores that require a private network path.
Revyl provisions the runner, keeps it assigned to your organization, routes jobs to that runner, streams logs, registers the resulting build, and cleans up temporary source archives. Enterprise capacity can be backed by dedicated M4-class Mac hosts for iOS and dedicated Google Compute Ubuntu runners for Android.

Get Access

Remote builds require a dedicated build runner provisioned for your organisation. If you don’t have one yet, book a call to get set up:

Book a Demo

Schedule a call to provision dedicated build runners for your org.

Prerequisites

Before using remote builds, make sure you have:
  • Revyl CLI installed and authenticated. See CLI docs.
  • An app registered in Revyl. Create one from the Apps page or via the CLI.
  • A .revyl/config.yaml build command for the target platform.
  • A dedicated build runner provisioned for your org. Revyl confirms runner availability before uploading source.
If your build uses only public dependencies, that is usually enough. If your build needs private dependencies, see Private Dependencies.

Configuration

Add a build section to your .revyl/config.yaml:
build:
  system: Xcode
  platforms:
    ios:
      app_id: "uuid-of-your-app"
      setup: "npm install && cd ios && pod install"
      command: "xcodebuild -workspace ios/App.xcworkspace -scheme App -sdk iphonesimulator -configuration Debug -derivedDataPath build"
      output: "build/Build/Products/Debug-iphonesimulator/App.app"
      scheme: "App"
    android:
      app_id: "uuid-of-your-android-app"
      setup: "pnpm install"
      command: "./gradlew assembleDebug"
      output: "app/build/outputs/apk/debug/app-debug.apk"
FieldRequiredDescription
build.systemNoBuild system label, such as Xcode, ReactNative, Expo, or Bazel.
build.platforms.ios.app_idYesUUID of your app in Revyl. Find it on the Builds page or create it with the CLI.
build.platforms.ios.commandYesBuild command to run on the remote Mac runner. Remote builds are optimized for iOS simulator builds that produce an .app.
build.platforms.ios.setupNoPre-build setup command, such as dependency install or package-manager auth setup.
build.platforms.ios.outputNoOptional simulator .app path or glob. Remote iOS builds use it first, then fall back to DerivedData discovery.
build.platforms.ios.schemeNoXcode scheme. The CLI can also override this with --scheme.
build.platforms.android.app_idYes for AndroidUUID of your Android app in Revyl.
build.platforms.android.commandYes for AndroidBuild command that produces an emulator-installable APK. V1 rejects AAB artifacts.
build.platforms.android.setupNoPre-build setup command, such as dependency install.
build.platforms.android.outputRecommendedAPK path or glob relative to the source root. Required when multiple APKs are produced.
If you don’t have a config file, the CLI will auto-detect your Xcode project structure. For the full .revyl/config.yaml schema, see the Project Configuration reference.

Command Examples

React Native with CocoaPods:
build:
  platforms:
    ios:
      app_id: "uuid-of-your-app"
      setup: "npm install && cd ios && pod install"
      command: "xcodebuild -workspace ios/App.xcworkspace -scheme App -sdk iphonesimulator -configuration Debug -derivedDataPath build"
Xcode project without a workspace:
build:
  platforms:
    ios:
      app_id: "uuid-of-your-app"
      command: "xcodebuild -project App.xcodeproj -scheme App -sdk iphonesimulator -configuration Debug -derivedDataPath build"
Swift Package Manager dependencies:
build:
  platforms:
    ios:
      app_id: "uuid-of-your-app"
      setup: "xcodebuild -resolvePackageDependencies -workspace App.xcworkspace -scheme App"
      command: "xcodebuild -workspace App.xcworkspace -scheme App -sdk iphonesimulator -configuration Debug -derivedDataPath build"

Usage

# Remote build with config
revyl build remote --platform ios
revyl build remote --platform android

# Agent-safe JSON
revyl build remote --platform android --json

# Queue and return a build_job_id without waiting
revyl build remote --platform android --no-wait

# Clean build
revyl build remote --platform android --clean

# Compatibility alias
revyl build upload --remote --platform ios
revyl build upload --remote --platform android
The CLI will:
  1. Check that a build runner is available for your org
  2. Package the current working tree by default
  3. Upload the archive to Revyl
  4. Trigger the remote build
  5. Stream build logs until completion
  6. Register the resulting .app or .apk as a new build version
On success, the new version appears on your Builds page and can be used immediately for testing.

Large Repositories and Generated CI Pipelines

Remote builds use a source-upload flow: the CLI packages source, uploads that archive, and asks the dedicated runner to build it. revyl build remote includes dirty working-tree edits by default; pass --committed-only to build only the committed tree at HEAD. That path is useful for self-contained mobile projects, but it is not the best contract for very large monorepos, generated GitLab child pipelines, or build graphs that already rely on prebuilt artifacts and cache heuristics. For those systems, use an artifact-first handoff:
detect changes/cache state
  -> generate CI pipeline layout
  -> fan out build jobs
  -> produce simulator .app / .app.zip / APK artifacts
  -> revyl build upload --file or --url
  -> revyl test run / revyl workflow run
Example GitLab job after your existing build graph produces an iOS simulator artifact:
upload-to-revyl:
  stage: test
  needs: ["build-ios-simulator"]
  script:
    - revyl build upload --file build/App.app.zip --platform ios --app "$REVYL_IOS_APP_ID" --version "$CI_COMMIT_SHA" --set-current --json
    - revyl workflow run smoke-tests --no-wait
If your artifact is already in Artifactory, S3, GCS, GitLab artifacts, or another authenticated store, use URL ingestion instead:
revyl build upload \
  --url "$ARTIFACT_URL" \
  --header "Authorization: Bearer $ARTIFACT_TOKEN" \
  --app "$REVYL_IOS_APP_ID" \
  --version "$CI_COMMIT_SHA" \
  --set-current \
  --json
This keeps GitLab responsible for DAG orchestration and cache decisions while Revyl handles artifact validation, device installation, test execution, and reporting.

Repo-Backed Remote Builds

For large monorepos where you still want Revyl to run the mobile build step, use a repo-backed remote build instead of uploading a fresh source archive from the CLI. In this mode, Revyl’s dedicated runner keeps a cached checkout or mirror, fetches the requested ref, pulls the needed LFS objects, optionally checks out only the relevant subdirectory, and then runs the configured build command. The configuration shape is:
build:
  source:
    type: git
    repo_url: [email protected]:company/mobile-monorepo.git
    ref: "$CI_COMMIT_SHA"
    subdir: apps/ios
    lfs: true

  platforms:
    ios:
      app_id: "$REVYL_IOS_APP_ID"
      scheme: "App"
      command: "xcodebuild -workspace App.xcworkspace -scheme App -sdk iphonesimulator -configuration Debug -derivedDataPath build"
      output: "build/Build/Products/Debug-iphonesimulator/App.app"
Use this when Revyl should handle managed runner execution, but your repository is too large for per-build source upload. GitLab or another CI system can still decide which targets need to build; the selected jobs call the Revyl remote build runner for the platform-backed build execution. Repo-backed source settings require repository credentials, LFS access, and any private dependency/network configuration needed by the runner.

Bazel

Bazel is supported today through the same command/output contract used by local and CI uploads. Configure the Bazel target as the build command and point output at the generated mobile artifact:
build:
  system: Bazel
  platforms:
    ios:
      app_id: "uuid-of-ios-app"
      command: "bazel build //ios:MyApp -c dbg --ios_multi_cpus=sim_arm64"
      output: "bazel-bin/ios/MyApp_archive-root/Payload/MyApp.app"
Then upload from CI or a developer machine:
revyl build upload --platform ios --version "$CI_COMMIT_SHA" --set-current
For Bazel Remote Build Execution, the recommended v1 integration is still artifact-first: let your existing Bazel/RBE pipeline produce the simulator artifact, then upload the result to Revyl. Running Bazel itself on a Revyl-managed runner is possible as an enterprise/dedicated-runner integration, but it requires runner setup for your Bazel toolchain, repository access, remote cache/RBE credentials, and private network paths. Treat that as a first-class build adapter project rather than the default source-upload remote build flow.

Private Dependencies

Many enterprise mobile builds need private package mirrors, artifact stores, Git mirrors, license servers, or internal APIs. Revyl supports these with a dedicated runner plus a private network path.

Standard Path: No Private Dependencies

If your dependencies are reachable from the public internet, no extra customer networking is required:
  1. Revyl provisions a dedicated runner for your org.
  2. You configure .revyl/config.yaml.
  3. You run revyl build remote --platform ios or revyl build remote --platform android.
  4. Revyl uploads source, builds on the runner, and registers the .app or .apk.
If your build dependencies are private, Revyl can route runner traffic through AWS PrivateLink. Traffic path:
build tools on Revyl runner
  -> local runner proxy
  -> Revyl AWS proxy
  -> Revyl interface VPC endpoint
  -> your VPC endpoint service
  -> your NLB
  -> private dependency
Customer inputs needed:
InputExample
First repo/build targetGitLab job, Xcode workspace/project, Gradle module, scheme/flavor
Build commandxcodebuild -workspace ios/App.xcworkspace -scheme App -sdk iphonesimulator -configuration Debug build or ./gradlew assembleDebug
Setup commandnpm install && cd ios && pod install, pnpm install, or project-specific setup
Expected artifactSimulator .app or Android .apk
Private dependency listArtifactory, npm mirror, Maven mirror, CocoaPods mirror, private Git, license server
AWS regionFor example us-west-2
Endpoint service namecom.amazonaws.vpce.<region>.vpce-svc-...
Ports/protocolsHTTPS 443, SSH 22, registry-specific TCP
DNS nameThe hostname the build tools should use
Credentials/secretsPackage manager tokens, certs, Git credentials
Revyl handles:
  • Dedicated runner provisioning, including M4-class Mac capacity for iOS and Ubuntu Android build runners.
  • Host setup for required Xcode, Android SDK/JDK/Gradle, proxy, and dependency configuration.
  • Runner org labels and queue routing.
  • Source upload, log streaming, build registration, and cleanup.
  • Consumer interface endpoint setup after your team authorizes Revyl’s AWS principal.
  • Runner proxy configuration and validation from the Mac host.
You handle:
  • Choosing the first build target.
  • Exposing the private dependency through your endpoint-service pattern.
  • Authorizing Revyl’s AWS principal.
  • Providing dependency DNS/auth details.
  • Confirming whether cross-region PrivateLink is allowed when Revyl and your services are in different AWS regions.
Cloudflare Tunnel can be used as a temporary outbound-only speed path if your team asks for it, but PrivateLink is the usual enterprise path when your security team already has AWS endpoint-service patterns.

Optional: Private Access to the Revyl API

The PrivateLink path above is for build dependencies that the Revyl runner must reach. Some teams also require their CI system to call the Revyl API or upload source archives through a private path. Treat that as a separate control-plane setup:
  • Your CI still uses the same Revyl CLI commands.
  • Revyl provides the API endpoint details for the private path.
  • Your networking team confirms DNS, endpoint policy, and allowed VPCs.
  • The build-runner dependency path and the CI-to-Revyl API path can be rolled out independently.

Troubleshooting

No build runner available

No android build runners are available for your organisation.
Your org doesn’t have a build runner provisioned for that platform, or it’s temporarily offline. Try again in a few minutes, or book a call to get a runner set up.

Private dependency unreachable

Symptoms:
  • npm install, pod install, swift package resolve, or artifact downloads hang or fail.
  • The build log shows DNS failures, TCP timeouts, proxy errors, or package-manager authentication errors.
Check:
  • The endpoint service is approved for Revyl’s AWS principal.
  • The endpoint connection is accepted and healthy.
  • The dependency hostname resolves on the runner through the intended private path.
  • The required port is open from the endpoint service to the NLB target.
  • Package-manager credentials are available to the build.

Artifactory, npm, Maven, CocoaPods, or SPM auth fails

Remote builds run in a clean runner environment. Make sure your setup command can access the same credentials your CI job uses:
  • npm: .npmrc or environment token.
  • Maven/Gradle: settings.gradle, gradle.properties, or repository credentials.
  • CocoaPods: private specs repo access and Git credentials.
  • Swift Package Manager: Git/token access for private packages.
  • Artifactory: registry URL, token, and certificate configuration.

Build timeout

Remote builds have a 30-minute execution timeout. If your build consistently times out:
  • Use --clean sparingly (cold builds are slower)
  • Check if your setup command installs unnecessary dependencies
  • Consider splitting large monorepos into smaller build targets

Wrong scheme or missing artifact

If the build exits successfully but no simulator .app is registered:
  • Confirm the scheme builds a simulator app, not only tests or a device-only target.
  • Include -sdk iphonesimulator.
  • Confirm the expected product appears under Xcode DerivedData.
  • Try --scheme <SchemeName> if your config omits the scheme.
If an Android build exits successfully but no APK is registered:
  • Confirm the command produces an APK, not an AAB.
  • Set build.platforms.android.output when multiple APKs are produced.
  • Use a debug/emulator-installable artifact for V1.

Working-tree changes

revyl build remote includes dirty working-tree edits by default so agent loops can build the code they just changed. Use --committed-only when you want the old committed-tree behavior. The compatibility alias revyl build upload --remote keeps its legacy committed-only default unless you pass --include-dirty.

Next Steps

Builds Overview

Manage builds and versions

iOS Simulator Builds

Build locally with Xcode

CI/CD

Automate builds in your pipeline

Need help? Contact [email protected]