Skip to content
NewiPhone Duo, live in your browser. Try it →

Cloud builds Cloud builds

Cloud Builds

Build iOS, Android, Apple TV and Android TV apps in the cloud, no Mac: simulator builds for cloud devices, signed builds for TestFlight and Google Play.

Build your app for iOS, Android, Apple TV (tvOS) and Android TV in the cloud straight from the CLI, with no Mac, no CI pipeline and no local toolchain. React Native, Expo, Flutter, Capacitor (Ionic), Kotlin Multiplatform and native Xcode or Gradle projects all build; see Frameworks for what each one needs. A cloud build makes one of two things:

  • a development build (a simulator or emulator build) that runs on a cloud device in VibeView, for testing and for vibeview dev;
  • a signed production build (an .ipa for the App Store, an .aab or .apk for Google Play) that you ship. vibeview submit then uploads it to TestFlight or a Google Play track for you.

To build on every push from GitHub Actions or another CI, see Cloud builds in CI.

What you can build

BuildCommandWhat you getWhere it goes
Developmentvibeview build --cloudA simulator build (iOS, tvOS) or emulator build (Android, Android TV)Your app’s Build history, ready to run on a cloud device
Productionvibeview build --cloud --productionA signed .ipa (iOS, tvOS) or .aab/.apk (Android, Android TV)The Builds page, to download or submit
Store uploadvibeview submitA succeeded production build, uploaded for youTestFlight (iOS, tvOS) or a Google Play track (Android, Android TV)

Production builds need a signing credential. You can upload the one you already have, or let VibeView generate it: an Android keystore, or iOS and tvOS certificates and profiles through your App Store Connect API key. See App Signing & Production Builds and Store Submission. Production builds can also number themselves (see Automatic build numbers).

Roku channels cannot be cloud-built: a channel is a zip of source files, so upload it as usual.

Configure your project

Cloud builds use the same vibeview.json build block that vibeview dev --build and local vibeview build use. Each platform entry needs a build.command (the shell command that produces your build) and a build.artifact (the project-relative path of the result; * is allowed in the file name).

If a platform has no build block yet, vibeview build --cloud asks for both values and saves them to vibeview.json straight away, so a failed build never costs you the answers. Under --json or in a non-interactive shell it reports the missing config as an error instead of prompting.

Make a build that runs on VibeView devices

The build block makes the development build, so its command must produce something a VibeView simulator or emulator can install:

  • iOS and Apple TV: a simulator .app, built with xcodebuild -sdk iphonesimulator (or -sdk appletvsimulator) and CODE_SIGNING_ALLOWED=NO. An archive or an .ipa is a device build and can’t run on a simulator.
  • Android and Android TV: an .apk, for example from ./gradlew assembleDebug (an .aab can’t be installed). If the app has native code, keep x86_64 in the build, and x86 too for Android TV: don’t filter them out with abiFilters, reactNativeArchitectures or ABI splits. See Architecture.

The signed build you send to the stores is a separate command: see Production builds.

Install your dependencies as part of the build command

Your build runs in a clean environment that has only your source. Dependency directories are not uploaded — node_modules, ios/Pods and .git are always excluded, because they are large, machine-specific, and reproducible from your lockfile. Nothing installs them for you, so your build command has to do it:

{
  "build": {
    "command": "yarn install --immutable && cd android && ./gradlew assembleDebug",
    "artifact": "android/app/build/outputs/apk/debug/*.apk"
  }
}

Use whatever your project already uses — yarn install --immutable, npm ci, pnpm install --frozen-lockfile — and add pod install for a native iOS build. Flutter is the exception: flutter build fetches your packages itself. A command that skips this step fails early with an error like Couldn't find the node_modules state file or Cannot find module, which means the dependencies were never installed rather than anything wrong with your project.

If installing needs a private registry token, store it as a build secret rather than putting it in the command — see Build Variables & Secrets.

Which Xcode iOS builds use

Every iOS cloud build uses the same Xcode, a stable release. Beta releases of Xcode are not offered, and you can’t pick a different version for one build. The build log starts with the Xcode version and the iOS SDK version your app was compiled against.

An app built with an SDK older than the iOS version on the device behaves the way that older SDK tells it to. On the iPhone Duo, for example, it runs in a phone-sized window on the inner screen instead of filling it. If you need a newer SDK than the cloud builders have, build the app on your own Mac and add it with vibeview upload-app (see CLI). App Store Connect doesn’t accept builds made with a beta Xcode.

Which Node.js cloud builds use

iOS and Android cloud builds both run on Node.js 22 (LTS). You can’t pick a different version for one build: an .nvmrc, .node-version or engines.node entry in your project doesn’t change it. The build log prints the exact Node.js version before your build command runs.

Which Java Android builds use

Android cloud builds run Gradle on Java 17, and Java 21 is installed alongside it. A project that asks Gradle for a Java 21 toolchain (jvmToolchain(21) or languageVersion = 21) gets it with no extra setup.

A project that needs Gradle itself to run on Java 21 sets JAVA_HOME for the Gradle call. Capacitor 7 and later set the Java language level to 21 without a toolchain, so their Gradle build fails on Java 17 with invalid source release: 21. Android builders set JAVA_HOME_17 and JAVA_HOME_21 to each installed Java:

cd android && JAVA_HOME=${JAVA_HOME_21:-$JAVA_HOME} ./gradlew assembleDebug

The :-$JAVA_HOME part keeps a local build on your own Java. Other Java versions are not available. The build log lists the installed Java versions before your build command runs, and a build that fails with invalid source release says which variable to use.

iOS builders have Java 21 too, for projects whose Xcode build runs Gradle (Kotlin Multiplatform).

Which Ruby cloud builds use

iOS and Android cloud builds have Ruby with Bundler, so a build command can install the gems in your Gemfile and run them, for example Fastlane or the CocoaPods version your Gemfile pins. To use your Gemfile’s CocoaPods, replace the pod install step of your build command:

bundle install && cd ios && bundle exec pod install && cd ..

Run a Fastlane lane the same way, with bundle exec fastlane <lane>.

  • iOS builds run Ruby 3.3.10, and Ruby 2.7.8, 3.4.9 and 4.0.3 are installed too. A .ruby-version file at your project root that names one of them exactly (for example 3.4.9, not 3.4) selects it. If it names any other Ruby, the build uses 3.3.10 and the build log says so. A .ruby-version in a subfolder must name an installed Ruby.
  • Android builds run Ruby 3.3.10. A .ruby-version file doesn’t change it.
  • Plain pod (without bundle exec) still runs the builders’ own CocoaPods.

bundle install runs on each build: nothing carries over between builds. The build log prints the Ruby version before your build command runs. If your Gemfile’s ruby line doesn’t accept that version, Bundler stops with Your Ruby version is ..., but your Gemfile specified ..., and the build’s error summary says what to change.

Which Flutter cloud builds use

iOS and Android cloud builds have the Flutter SDK installed, version 3.47.7 (stable), and flutter is on the path. The build log prints the version before your build command runs.

Cloud builds also have FVM, so a build can use another Flutter release:

  • Your project pins its release with FVM. Commit the pin (.fvmrc) and run Flutter as fvm flutter in the build command, for example fvm flutter build apk --debug. FVM then uses the release the pin names; a command that runs plain flutter still gets the default. The build log names the pin it finds under the default’s version. vibeview build --cloud suggests the fvm flutter commands when it finds a pin that’s part of your upload (CLI 0.43.0 and later); if you set up the project with an earlier CLI, change the saved build command in vibeview.json yourself. The older .fvm/fvm_config.json works too, as long as it’s uploaded: newer FVM setups ignore .fvm/, and an ignored file isn’t uploaded.
  • No FVM in your project. Name the release in the build command instead, for example when your project needs an older release than the default:
fvm spawn 3.44.9 build apk --debug

Flutter 3.47.7 and 3.44.9 are ready on the builders. Any other release is downloaded during the build, which adds about two minutes, and on Android it may also download its own Android NDK and Gradle, which adds a few more.

Older React Native and boost

React Native 0.72 and 0.73 download the boost library during pod install from an address that has since gone offline. iOS cloud builds fetch the same boost archive from boost’s current download site instead, so these projects install without changes.

Frameworks

FrameworkSuggested buildNotes
Expo (managed)prebuild, then Xcode or GradleExpo managed projects
React NativeXcode or Gradle, after installing dependenciesBare React Native projects
Native Xcode or Gradlexcodebuild or the Gradle wrapperNative iOS and Android projects
Flutterflutter buildFlutter projects
Capacitor, Ionicweb build and cap sync, then Xcode or GradleCapacitor and Ionic projects
Kotlin MultiplatformGradle for Android, Xcode for iOSKotlin Multiplatform projects

Other frameworks may build too when their tools install from the build command, since a cloud build runs any shell command. For a development build, the command has to produce a simulator .app or an emulator .apk. NativeScript doesn’t build on the cloud builders yet.

Expo managed projects

A managed Expo project — one with no ios/ or android/ directory — needs no extra setup. The suggested build commands generate the native project during the build itself: they install dependencies, run npx expo prebuild for the platform, then drive the native build directly (xcodebuild for the simulator .app, Gradle for the debug .apk). Keep the generated ios/ and android/ directories out of git — prebuild recreates them on every build.

Do not use npx expo run:ios or npx expo run:android as a cloud build command. Those are dev-loop commands that build and install onto a booted simulator or a connected Android device, so on a cloud builder they fail before compiling anything.

For an iOS build, the suggested command derives the Xcode workspace and scheme from the name in your app.json (MyApp in the example below). If your project uses a dynamic config (app.config.js, app.config.ts, app.config.mjs or app.config.cjs), the CLI asks your project’s installed Expo for the name, which runs your config on your machine just as npx expo start does. Install your project’s dependencies first; the CLI never downloads Expo. The name comes from your local environment, so if your config reads it from an environment variable or a local .env file, set the same variable for the cloud build (in build.env or as a build variable). When the name can’t be read, the CLI says why and suggests no iOS command; write your build command by hand instead.

The install step follows your lockfile: npm ci, yarn install --immutable, or pnpm install --frozen-lockfile. Cloud builders don’t have Bun, so a project with a Bun lockfile gets npm install, as does a project with no lockfile.

You do not need an existing app. If your project has never had a build uploaded, the finished build creates the app for you — its name, package identifier, platform and icon all come from the build itself, so nothing to type and nothing to get wrong. When you already have apps for that platform, vibeview build --cloud asks which one these builds should attach to, with “Create a new app from this build” offered alongside. Either way the answer is saved to vibeview.json as appId, so later builds go to the same app.

{
  "defaultPlatform": "ios",
  "platforms": {
    "ios": {
      "appId": "app_abc123",
      "build": {
        "command": "npm ci && npx expo prebuild --platform ios && xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build CODE_SIGNING_ALLOWED=NO",
        "artifact": "ios/build/Build/Products/Debug-iphonesimulator/*.app",
        "env": { "APP_VARIANT": "development" }
      }
    },
    "android": {
      "appId": "app_abc123",
      "build": {
        "command": "npm ci && npx expo prebuild --platform android --no-install && cd android && ./gradlew assembleDebug",
        "artifact": "android/app/build/outputs/apk/debug/*.apk"
      }
    }
  }
}

For a complete, working example of this config — debug and production commands for both platforms, automatic build numbers, and the signing plugins a managed project needs — see the Kitlist sample app, a small Expo project that was built and shipped entirely through cloud builds.

Bare React Native projects

For a React Native project with its ios/ and android/ directories checked in, the suggested build commands install your dependencies first, then build natively. iOS runs pod install in ios/ before xcodebuild, because ios/Pods is never uploaded:

{
  "defaultPlatform": "ios",
  "platforms": {
    "ios": {
      "build": {
        "command": "yarn install --immutable && cd ios && pod install && cd .. && xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build CODE_SIGNING_ALLOWED=NO",
        "artifact": "ios/build/Build/Products/Debug-iphonesimulator/*.app"
      }
    },
    "android": {
      "build": {
        "command": "yarn install --immutable && cd android && ./gradlew assembleDebug",
        "artifact": "android/app/build/outputs/apk/debug/*.apk"
      }
    }
  }
}

The workspace and scheme come from the ios/*.xcworkspace in your project, and the install step follows your lockfile, as for Expo. The suggested command runs the CocoaPods installed on the cloud builders, not one pinned in your Gemfile; to use your Gemfile’s, see Which Ruby cloud builds use.

Native iOS and Android projects

A native app (Swift or Objective-C in Xcode, Kotlin or Java with Gradle) builds with the tools the cloud builders already have: Xcode and CocoaPods for iOS and Apple TV, the Android SDK with Java 17 and 21 for Android.

vibeview build --cloud suggests the commands when it can tell what to build (CLI 0.42.0 and later):

  • iOS: the one Xcode project at the root of your repository or one folder down, built with its shared scheme, or with its app target’s name when it doesn’t share one. A Podfile next to the project adds pod install and builds the workspace.
  • Android: the Gradle wrapper at the root of your repository, building the one module that applies the Android application plugin. With the wrapper in an android/ folder and no package.json in the repository, it suggests cd android && ./gradlew assembleDebug.

When there are several projects, schemes or app modules, it asks you to enter the command instead. Either way, the commands end up in vibeview.json:

{
  "defaultPlatform": "ios",
  "platforms": {
    "ios": {
      "build": {
        "command": "xcodebuild -project MyApp.xcodeproj -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath build CODE_SIGNING_ALLOWED=NO",
        "artifact": "build/Build/Products/Debug-iphonesimulator/*.app"
      }
    },
    "android": {
      "build": {
        "command": "./gradlew :app:assembleDebug",
        "artifact": "app/build/outputs/apk/debug/*.apk"
      }
    }
  }
}
  • If the iOS project uses CocoaPods and doesn’t commit its Pods folder, start the command with pod install && and build the workspace (-workspace MyApp.xcworkspace). Swift packages are resolved during the build.
  • For Apple TV, use -sdk appletvsimulator; the app lands in Debug-appletvsimulator.
  • Commit the Gradle wrapper (gradlew and gradle/wrapper/): the build runs the wrapper, which downloads your project’s Gradle version.
  • Run vibeview build from the directory these paths start from, usually the repository root.

Flutter projects

A Flutter app builds with the Flutter SDK the cloud builders have (see Which Flutter cloud builds use). vibeview build --cloud recognizes a Flutter project by a pubspec.yaml that depends on the Flutter SDK and suggests these commands (CLI 0.42.0 and later):

{
  "defaultPlatform": "android",
  "platforms": {
    "android": {
      "build": {
        "command": "flutter build apk --debug",
        "artifact": "build/app/outputs/flutter-apk/app-debug.apk"
      }
    },
    "ios": {
      "build": {
        "command": "flutter build ios --simulator --debug",
        "artifact": "build/ios/iphonesimulator/Runner.app"
      }
    }
  }
}
  • flutter build fetches your packages and regenerates the files Flutter’s own .gitignore leaves out, such as the Android Gradle wrapper, so the command needs no install step.
  • If your project pins its Flutter release with a committed FVM config, the suggested commands start with fvm flutter in place of flutter (CLI 0.43.0 and later).
  • A debug build runs on VibeView devices as it is: it doesn’t need flutter run or a debugger attached.
  • For a production build, the suggested Android command is flutter build appbundle --release (artifact build/app/outputs/bundle/release/app-release.aab), signed with the keystore settings described in App Signing. For iOS the CLI suggests no production command, so write your own. Flutter’s .gitignore leaves out files the iOS build needs (ios/Flutter/Generated.xcconfig among them), so start the command with flutter build ios --config-only --release to regenerate them, run pod install in ios/ if it has a Podfile, then archive ios/Runner.xcworkspace with the signing settings App Signing lists.
  • Flutter doesn’t support Apple TV. For Android TV the CLI suggests no development build command, so enter your own. Flutter builds no 32-bit x86 code, so a Flutter app runs only on the Android TV devices that run x86_64.

Capacitor and Ionic projects

A Capacitor app (including Ionic apps that use Capacitor) needs its web app built and copied into the native projects before the native build. vibeview build --cloud recognizes the project by its capacitor.config file or the @capacitor/core dependency and suggests commands that do all of it (CLI 0.42.0 and later):

{
  "defaultPlatform": "android",
  "platforms": {
    "android": {
      "build": {
        "command": "npm ci && npm run build && npx cap sync android && cd android && JAVA_HOME=${JAVA_HOME_21:-$JAVA_HOME} ./gradlew assembleDebug",
        "artifact": "android/app/build/outputs/apk/debug/*.apk"
      }
    },
    "ios": {
      "build": {
        "command": "npm ci && npm run build && npx cap sync ios && xcodebuild -project ios/App/App.xcodeproj -scheme App -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build CODE_SIGNING_ALLOWED=NO",
        "artifact": "ios/build/Build/Products/Debug-iphonesimulator/*.app"
      }
    }
  }
}
  • Commit the android/ and ios/ folders that npx cap add creates. The build syncs your web app into them but doesn’t create them.
  • The web build step runs your build script, and is left out when package.json has none. The install step follows your lockfile.
  • Capacitor 7 and later need Gradle to run on Java 21, which is what the JAVA_HOME=... part does (see Which Java Android builds use). The CLI adds it when the project’s Gradle wrapper is 8.5 or later, which Capacitor 7 and later ship. Capacitor 6’s older Gradle can’t run on Java 21, so its suggested command leaves it out. If you upgrade Capacitor later, add the JAVA_HOME=... part to the saved command yourself.
  • An iOS project that uses CocoaPods (a Podfile in ios/App) builds the workspace, -workspace ios/App/App.xcworkspace. npx cap sync installs the pods.

Kotlin Multiplatform projects

A Kotlin Multiplatform app with a shared module and Compose Multiplatform or native UI builds like a native project. Gradle builds the Android app, and Xcode builds the iOS app, running Gradle to compile the shared Kotlin code. For a project laid out like JetBrains’ template, vibeview build --cloud suggests (CLI 0.42.0 and later):

{
  "defaultPlatform": "android",
  "platforms": {
    "android": {
      "build": {
        "command": "./gradlew :androidApp:assembleDebug",
        "artifact": "androidApp/build/outputs/apk/debug/*.apk"
      }
    },
    "ios": {
      "build": {
        "command": "xcodebuild -project iosApp/iosApp.xcodeproj -scheme iosApp -configuration Debug -sdk iphonesimulator -derivedDataPath iosApp/build CODE_SIGNING_ALLOWED=NO",
        "artifact": "iosApp/build/Build/Products/Debug-iphonesimulator/*.app"
      }
    }
  }
}

Every iOS build downloads the Kotlin/Native compiler before it compiles, which adds a few minutes. The Android module is androidApp in the example; JetBrains’ wizard has also generated projects that call it composeApp. The CLI uses whichever module your project has.

A project that links the shared module through CocoaPods (a Podfile next to the Xcode project) gets no suggested iOS command: pod install needs the shared framework that Gradle generates first, so the command has to run that Gradle task before pod install.

Build environment variables

The optional build.env map sets plain environment variables for the build command — useful for non-secret configuration like APP_VARIANT. Keys must be valid environment variable names (letters, digits, underscores; not starting with a digit). Treat build.env values like the rest of your vibeview.json: they travel with your build request in plain text and are visible to anyone in your organization, so it’s a good place for a build flavor or feature flag, not an API key or credential.

For credentials, tokens, service-account files, and signing material, use build variables and secrets instead — see Build Variables & Secrets for how to store them, the three visibility levels, and how to upload a file (such as a keystore or a service-account JSON). If a stored variable has the same name as an entry in build.env, the stored variable wins for that build.

Your project’s .env files follow the same rule as the rest of your source: anything ignored by .gitignore is not uploaded.

Start a build

vibeview build --cloud                  # defaultPlatform
vibeview build --cloud --platform android
vibeview build --cloud --platform all   # every configured platform with a build block

The CLI packs your project source — every file git tracks plus untracked files that are not ignored by .gitignore — uploads it, and streams the build log until the build finishes. Anything .gitignore matches stays on your machine, but an untracked file that is not gitignored (a stray credentials file, for example) is uploaded, so make sure secret files are covered by .gitignore. Add a .vvignore file (same syntax as .gitignore) to exclude additional paths from the upload. node_modules/, ios/Pods/, and .git/ are always excluded, and the packed source may not exceed 2 GB. Each build runs in a clean, isolated environment that is destroyed when the build ends.

Run vibeview build --cloud from your project root: only the directory you run it in (and what’s below it) is uploaded. Before uploading, the CLI checks your build command against the files it is about to send:

  • If the command installs dependencies or runs a package script in the uploaded directory (npm ci, npm install, npm run …, yarn, yarn install, pnpm install, bun install and the like) but that directory has no package.json, the CLI stops with No package.json in <dir>. Run vibeview build from your project root. Nothing is uploaded and nothing is charged. Under --json this is an error event with code: "no_package_json". Steps after the command changes directory (cd apps/mobile && yarn install ...) aren’t checked, and neither are npx runs or global installs (npm i -g …).
  • If your project has a lockfile but the command never installs dependencies (no npm ci, npm install, yarn, yarn install or pnpm install), the CLI prints a warning suggesting the install command for your package manager, then builds anyway. Running a script such as yarn build:ios doesn’t count as installing. A Yarn Plug’n’Play project that commits its .pnp.cjs (zero-install) gets no warning.
  • If the upload has a package.json but no lockfile and the command installs, the CLI warns: an install that requires a lockfile (npm ci, yarn install --immutable, --frozen-lockfile) fails without one. Commit your lockfile.
  • Cloud builders don’t have Bun. If the command runs bun or bunx, the CLI warns that the step will fail; use npm, Yarn or pnpm instead.
  • If you run the command from a subdirectory of your repository that has no package.json while a directory above it does, the CLI warns that only the subdirectory will be uploaded and names the directory that looks like your project root.

Warnings never stop the build. Under --json each one is a warning event with platform and message, one per affected platform.

Useful flags:

FlagEffect
--no-waitPrint the build ids and exit instead of streaming logs.
--downloadAfter a build succeeds, download its artifact to the current directory (implies waiting).
--jsonEmit NDJSON events (build_created, status, build_log, build_finished, artifact_saved, skipped, warning, error) instead of human output.

A status event reports a build’s progress before its log starts: once with status: "queued" if the build is waiting for a build slot, and once with status: "running" when it starts. A skipped event (with platform and reason) is emitted only by --platform all --production, for each configured platform that can’t be production-signed — those platforms are left out while the rest still build.

Exit code is 0 when every requested build succeeds, 1 otherwise — including when a --download fails.

Manage builds

vibeview builds list                    # recent cloud builds in your organization
vibeview builds logs <id>               # print a build's log (exit 0 always)
vibeview builds logs <id> --follow      # stream until done; exit 0 only if it succeeded
vibeview builds download <id> [--out d] # download a succeeded build's artifact
vibeview builds cancel <id>             # cancel a queued or running build
vibeview builds delete-artifact <id>    # delete a production build's stored artifact file

delete-artifact is irreversible — the artifact can’t be regenerated without running a new build — but the build’s record, logs, and cost stay intact; only the downloadable file is removed.

The Builds page

The Builds page in the dashboard has four tabs:

  • Activity (the default) — every cloud build in your organization, with status, logs, and a Download action (and Delete for production artifacts). Builds are grouped by app: each app gets a card, and apps you have linked as the same product (see Same app on other platforms) share one card. The card names each platform with its latest successful build among the builds shown (the latest 50), so an iOS and an Android app with the same name are easy to tell apart. A platform shows that build’s version, or its date when the build has no version. Each row starts with the version and build number. A build that never produced one (most failed or canceled builds, and production builds without automatic build numbers) starts with its build id instead. A card shows its three newest builds; Show N older builds lists the rest. Switch View to Timeline to see every build in one list, newest first, with the app named on each row. Above the list, a row of counts shows how many builds are running and queued right now, how many failed in the last 24 hours, how many succeeded in the last 7 days, and how many builds you have run this month. Status and Type filters at the top of the page narrow the list, and the search box finds builds by app name, product name, version, build number or build id. When a build fails because of a signing credential (missing or expired), its error shows a Fix in Signing link that jumps straight to the Signing tab. Members who can start builds (Developer and up) can click Start a build at the top of the page at any time: it opens a prompt for Claude Code, Codex, Cursor or another coding agent that adds the build block to vibeview.json and starts the builds, which then show up here. Before your organization’s first build, the tab shows those members the three ways to start one: Ask your agent (Get the prompt gives the same prompt), the terminal command, and a link to this guide. Once builds exist, a one-line hint under the list offers the same prompt for another platform.
  • Signing — the signing credentials your production builds use: view, upload, rotate, and delete them. See App Signing & Production Builds.
  • Variables — the build variables and secrets injected into your cloud builds (see below).
  • Webhooks — notify your own systems the moment a build finishes. See Build Webhooks.

Managing build variables in the dashboard

The Variables tab shows every stored build variable — name, visibility, scope, and when it was last updated — and lets you add, edit, and delete them without the CLI. Any member of your organization can view the list; adding, editing, and deleting require the Admin role.

Adding or editing a variable works like vibeview env set: choose a name, a value (or upload a file), a visibility level, and a scope (org-wide or a specific app). See Build Variables & Secrets for what the visibility levels mean and the limits that apply.

The tab never lists full values back: at most the last 4 characters of a plain or sensitive value are shown, and secrets and files show nothing at all. A secret can only be replaced with a new value, never viewed; a file variable can’t be edited in place — delete it and add it again.

Precedence is the same everywhere: an app-scoped variable overrides an org-wide variable of the same name, and both override a build.env entry of the same name in vibeview.json. Each app’s Build settings tab shows the effective set that reaches that app’s builds after this merge, alongside a preview of what its production builds would sign with.

Build details

Opening a build from the Activity tab — its id or a Details link — shows everything about that one build: its status and a timeline (queued, started, finished), how long it took, what it cost, the artifact download, and the build log with a search field, a Follow toggle, and a raw view for copying or sharing the full output. Production builds also show their store submissions, and whether signing material was attached and which certificate or profile it used. If your organization has any webhook endpoints configured, the build’s page lists the deliveries sent for it.

When your build command fails, the build’s error names the error found in its log, for example a CocoaPods, Gradle, npm or Xcode error line, and for common problems adds what to change: a missing package.json, a dependency install step left out of the command, a pod that needs a higher iOS deployment target, a tool or package that doesn’t support the builder’s Node.js version, or a Java version the builder doesn’t have. If the build was killed before it finished, most often because it ran out of memory, the error says so instead. The full log is always one click away for the rest.

A succeeded simulator build also becomes a build of its app: added to app builds on its timeline links to it in the app’s Build history, and Run this build opens Devices with that app and build selected. If that app build is later deleted from Build history, the page says so, and the build has nothing left to download or run.

Production builds

Add --production to build a signed release artifact instead of a simulator/emulator build:

vibeview build --cloud --production

Production builds are supported for ios, android, tvos, and androidtv. Android TV release builds sign with your Android credential — one keystore covers both form factors. tvOS builds sign with a tvOS credential (the same Apple distribution certificate, paired with a tvOS provisioning profile) — see App Signing & Production Builds.

This needs a signing credential uploaded first, and behaves differently from a regular cloud build in one important way: the artifact is downloaded, not installed. It does not appear in Build history and can’t be run on a device in VibeView. See App Signing & Production Builds for uploading credentials, expiry, and the Android build.gradle change.

Automatic build numbers

Apple and Google both refuse an upload that reuses a build number, and a forgotten bump costs a full rebuild. VibeView can manage the number for you: set autoIncrement in the platform’s build block, and every production build for that app gets the next number in a per-app, per-platform counter that VibeView keeps for you:

{
  "platforms": {
    "ios": {
      "appId": "my-app-abc123",
      "build": { "...": "...", "autoIncrement": true }
    }
  }
}

The assigned number reaches your build as the VIBEVIEW_BUILD_NUMBER environment variable — wire it into your project once:

// Expo: app.config.js / app.config.ts
ios: { buildNumber: process.env.VIBEVIEW_BUILD_NUMBER },
android: { versionCode: parseInt(process.env.VIBEVIEW_BUILD_NUMBER ?? '1', 10) },
// Bare React Native: android/app/build.gradle
versionCode (System.getenv("VIBEVIEW_BUILD_NUMBER") ?: "1").toInteger()

autoIncrement needs the platform linked to an app (appId) and applies to production builds only. For an app that already has builds in a store, seed the counter once so the next number lands above what’s out there:

vibeview versions list --app my-app-abc123
vibeview versions set --app my-app-abc123 --platform ios 42

For iOS and tvOS there’s a shortcut: when your organization has an App Store Connect key stored, the very first allocation checks your app’s highest existing build number and starts above it automatically — no seeding needed. The check covers your app’s 200 most recent uploads: for an app with a longer history whose highest build number is older than that, seed the counter once with vibeview versions set instead. If the lookup isn’t possible, the counter simply starts at 1.

Artifacts and Build history

A succeeded regular (non-production) cloud build registers automatically as a build of the configured app — it appears in the app’s Build history tab in the dashboard with a cloud badge, exactly like an uploaded build: you can run it on a device, share it, and use it with vibeview dev. A debug build works with vibeview dev and hot reload even when it embeds its JavaScript bundle, as Expo projects without expo-dev-client do: it loads from your Metro when Metro is running. To open a release build as it is, with no Metro, use vibeview dev --standalone --build <id>. Builds can be downloaded from the dashboard (Download button) or with vibeview builds download.

While a cloud build is queued or running — or if it finished without succeeding — it appears above the Build history list with its status and log. Unsuccessful builds stay visible there for a day.

A production build follows a different path: it shows up on the Builds page (not the app’s Build history) with a Download action once it succeeds — see App Signing & Production Builds.

Builds that can’t run on VibeView devices

A cloud build is never failed for what its output can do. If the build succeeds but its output can’t run on VibeView devices (for example an iOS build made for a physical device), it still succeeds and stays downloadable. Its build page says Built, but it can’t run on VibeView devices and shows why and how to fix it, and it appears in Build history as Can’t run: …. VibeView doesn’t offer it for sessions. Fix the cause in your project and build again. If the only problem is that no VibeView device can run it today (for example an Android build whose native code no device supports), it appears as No compatible device: starting a session with it is refused with the reason, and it starts working once a device that can run it is available. See When an upload is refused for the reasons and fixes.

Billing and limits

Each platform build is billed individually from your organization’s usage credit once it finishes. Your plan includes a monthly allowance of builds at no charge; builds beyond it draw from usage credit. You are not charged for builds canceled while still queued, or for builds that fail for reasons on our side.

Builds time out after 45 minutes on the Free plan and 2 hours on paid plans. One build runs at a time on Free, Dev, and Starter; two on Pro (additional builds queue). For custom terms, contact support@vibeview.io.

Build output counts toward your plan’s build storage (see Managing App Builds). A build is refused when you start it — and nothing is charged — if your organization is already at its storage limit. A build that is still waiting for a build slot when your organization reaches its storage limit is canceled before it starts and is not charged; your free allowance is not consumed. A build that has already started always keeps its output, even if that takes you over the limit. The output counts toward storage, so further uploads and builds are refused until you free space.

You can track your build usage under Settings → Plan and usage — builds run this month, how much of your free allowance is used, and build spend. Charged builds appear in its cost history labeled “Cloud build”.

While a build is waiting for a free slot, the CLI shows queued — waiting for a build slot and the dashboard shows a queued badge; the build’s time limit only starts counting once it actually starts. If no build capacity is available for an extended period, the build fails with a clear error instead of waiting forever — you are not charged and your free allowance is not consumed.

If the cloud builders for a platform are temporarily offline, a new build for that platform is refused immediately with a message saying so (for example iOS cloud builders are temporarily offline. Nothing was charged — try again in a few minutes.). Nothing is charged and your free allowance is not consumed; start the build again in a few minutes. If you submitted several platforms at once, none of them is started — the message names each platform that was held back and how to build the available ones on their own. A build that was already queued when the builders went offline fails after a few minutes with a similar message, also without charge. A brief restart of the builders does not refuse or fail builds — they wait in the queue and start once the builders are back.

Last updated 17 Oct 2026 Something wrong on this page? Tell us

Search the docs