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
.ipafor the App Store, an.aabor.apkfor Google Play) that you ship.vibeview submitthen 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
| Build | Command | What you get | Where it goes |
|---|---|---|---|
| Development | vibeview build --cloud | A simulator build (iOS, tvOS) or emulator build (Android, Android TV) | Your app’s Build history, ready to run on a cloud device |
| Production | vibeview build --cloud --production | A signed .ipa (iOS, tvOS) or .aab/.apk (Android, Android TV) | The Builds page, to download or submit |
| Store upload | vibeview submit | A succeeded production build, uploaded for you | TestFlight (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 withxcodebuild -sdk iphonesimulator(or-sdk appletvsimulator) andCODE_SIGNING_ALLOWED=NO. An archive or an.ipais a device build and can’t run on a simulator. - Android and Android TV: an
.apk, for example from./gradlew assembleDebug(an.aabcan’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 withabiFilters,reactNativeArchitecturesor 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-versionfile at your project root that names one of them exactly (for example3.4.9, not3.4) selects it. If it names any other Ruby, the build uses 3.3.10 and the build log says so. A.ruby-versionin a subfolder must name an installed Ruby. - Android builds run Ruby 3.3.10. A
.ruby-versionfile doesn’t change it. - Plain
pod(withoutbundle 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 asfvm flutterin the build command, for examplefvm flutter build apk --debug. FVM then uses the release the pin names; a command that runs plainflutterstill gets the default. The build log names the pin it finds under the default’s version.vibeview build --cloudsuggests thefvm fluttercommands 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 invibeview.jsonyourself. The older.fvm/fvm_config.jsonworks 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
| Framework | Suggested build | Notes |
|---|---|---|
| Expo (managed) | prebuild, then Xcode or Gradle | Expo managed projects |
| React Native | Xcode or Gradle, after installing dependencies | Bare React Native projects |
| Native Xcode or Gradle | xcodebuild or the Gradle wrapper | Native iOS and Android projects |
| Flutter | flutter build | Flutter projects |
| Capacitor, Ionic | web build and cap sync, then Xcode or Gradle | Capacitor and Ionic projects |
| Kotlin Multiplatform | Gradle for Android, Xcode for iOS | Kotlin 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
Podfilenext to the project addspod installand 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 nopackage.jsonin the repository, it suggestscd 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
Podsfolder, start the command withpod install &&and build the workspace (-workspace MyApp.xcworkspace). Swift packages are resolved during the build. - For Apple TV, use
-sdk appletvsimulator; the app lands inDebug-appletvsimulator. - Commit the Gradle wrapper (
gradlewandgradle/wrapper/): the build runs the wrapper, which downloads your project’s Gradle version. - Run
vibeview buildfrom 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 buildfetches your packages and regenerates the files Flutter’s own.gitignoreleaves 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 flutterin place offlutter(CLI 0.43.0 and later). - A debug build runs on VibeView devices as it is: it doesn’t need
flutter runor a debugger attached. - For a production build, the suggested Android command is
flutter build appbundle --release(artifactbuild/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.gitignoreleaves out files the iOS build needs (ios/Flutter/Generated.xcconfigamong them), so start the command withflutter build ios --config-only --releaseto regenerate them, runpod installinios/if it has aPodfile, then archiveios/Runner.xcworkspacewith 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/andios/folders thatnpx cap addcreates. The build syncs your web app into them but doesn’t create them. - The web build step runs your
buildscript, and is left out whenpackage.jsonhas 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 theJAVA_HOME=...part to the saved command yourself. - An iOS project that uses CocoaPods (a
Podfileinios/App) builds the workspace,-workspace ios/App/App.xcworkspace.npx cap syncinstalls 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 installand the like) but that directory has nopackage.json, the CLI stops withNo package.json in <dir>. Run vibeview build from your project root.Nothing is uploaded and nothing is charged. Under--jsonthis is anerrorevent withcode: "no_package_json". Steps after the command changes directory (cd apps/mobile && yarn install ...) aren’t checked, and neither arenpxruns 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 installorpnpm install), the CLI prints a warning suggesting the install command for your package manager, then builds anyway. Running a script such asyarn build:iosdoesn’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.jsonbut 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
bunorbunx, 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.jsonwhile 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:
| Flag | Effect |
|---|---|
--no-wait | Print the build ids and exit instead of streaming logs. |
--download | After a build succeeds, download its artifact to the current directory (implies waiting). |
--json | Emit 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.jsonand 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.