Vibe code to app stores
Android on Linux
12 min read
A step-by-step guide for shipping a vibe-coded React Native or Expo app to Google Play from a Linux machine — covering tool installation (Debian/Ubuntu and Fedora/RHEL), prebuild, signing, and the full Play Store submission process.
If you're using Lunadeck, the build and signing steps (Parts 4–6) are handled for you automatically. This guide is for building and publishing locally, or for understanding what Lunadeck does under the hood.
What you'll need before starting:
- A Linux computer (Debian/Ubuntu or Fedora-based)
- At least 12 GB of free disk space
- At least 8 GB of RAM
- A credit/debit card ($25 one-time Google Play fee)
- Your exported React Native / Expo project (ZIP from your AI coding tool, or a git clone)
- About 2–4 hours for initial setup, then ~3 weeks for the publishing process
Part 1: Install the Required Tools
You need three things: Node.js, Java (JDK 17), and Android Studio.
1.1 Install baseline utilities
Debian/Ubuntu:
sudo apt update
sudo apt install -y curl wget unzip gitFedora:
sudo dnf install -y curl wget unzip git1.2 Install Node.js (v18+)
Option A: Using nvm (recommended — works on all distros)
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Reload your shell
source ~/.bashrc # or source ~/.zshrc if you use zsh
# Install Node.js 22
nvm install 22
nvm use 22
# Verify
node --version # Should show v22.x.x
npm --versionOption B: Using your package manager
Debian/Ubuntu:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node --versionFedora:
curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
sudo dnf install -y nodejs
node --version1.3 Install Java (JDK 17)
Debian/Ubuntu:
sudo apt update
sudo apt install -y openjdk-17-jdk
java -versionFedora:
sudo dnf install -y java-17-openjdk-devel
java -version1.4 Install Android Studio
Step 1: Install prerequisite libraries
Debian/Ubuntu (64-bit):
sudo apt update
sudo apt install -y libc6:i386 libncurses5:i386 libstdc++6:i386 \
lib32z1 libbz2-1.0:i386 libfontconfig1 libfreetype6 \
libx11-6 libxext6 libxrender1 libxtst6 libxi6 \
unzip wgetFedora (64-bit):
sudo dnf install -y zlib.i686 ncurses-libs.i686 bzip2-libs.i686 \
libstdc++.i686 libX11 libXext libXrender libXtst \
fontconfig freetype unzip wgetStep 2: Download and install Android Studio
Download the Linux .tar.gz from https://developer.android.com/studio, then:
sudo tar -xzf ~/Downloads/android-studio-*.tar.gz -C /opt/
/opt/android-studio/bin/studio.shStep 3: First-time setup
When Android Studio launches:
- Choose "Do not import settings"
- Select "Standard" installation type
- Let it download the Android SDK, build tools, and emulator
Step 4: Install SDK components
Go to Tools > SDK Manager:
- SDK Platforms tab: check Android 14 (API 34) or newer
- SDK Tools tab: check Android SDK Build-Tools, Android SDK Command-line Tools, Android Emulator, Android SDK Platform-Tools
Step 5: Set environment variables
Add to your ~/.bashrc (or ~/.zshrc):
export ANDROID_HOME="$HOME/Android/Sdk"
export ANDROID_SDK_ROOT="$HOME/Android/Sdk"
export PATH="$PATH:$ANDROID_HOME/platform-tools"
export PATH="$PATH:$ANDROID_HOME/tools"
export PATH="$PATH:$ANDROID_HOME/tools/bin"
export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin"Then reload and verify:
source ~/.bashrc
adb --version1.5 Create a desktop shortcut (optional)
cat > ~/.local/share/applications/android-studio.desktop << 'EOF'
[Desktop Entry]
Name=Android Studio
Comment=Android IDE
Exec=/opt/android-studio/bin/studio.sh
Icon=/opt/android-studio/bin/studio.png
Terminal=false
Type=Application
Categories=Development;IDE;
EOFPart 2: Export and Prepare Your Project
2.1 Get your project files
If your AI tool provides a ZIP export, download and unzip it:
mkdir ~/my-app
cd ~/my-app
unzip ~/Downloads/your-project.zip -d .If you're working from a git repository:
git clone https://github.com/yourname/your-app.git ~/my-app
cd ~/my-app2.2 Install dependencies
cd ~/my-app
npm installCommon issues with AI-generated exports:
- Missing
.envvariables — create a.envfile with any required API keys - Dependency conflicts — delete
node_modulesandpackage-lock.json, then re-runnpm install - TypeScript errors — fix the specific file and line number the error points to
2.3 Confirm your project type
Open package.json and look at "dependencies":
- Contains
"expo"→ Expo managed workflow — follow Part 3A - Contains
"react-native"but not"expo"→ Bare React Native — follow Part 3B
Part 3A: Expo Managed Workflow
Skip to Part 3B if your project is bare React Native.
3A.1 Check your app.json
Open app.json and verify the "expo" section contains at minimum:
{
"expo": {
"name": "My App",
"slug": "my-app",
"android": {
"package": "com.yourname.myapp"
}
}
}The
android.packagevalue is permanent. Once you publish to Google Play, you cannot change it. Use a reverse-domain format:com.yourname.appname.
If android.package is missing, add it now.
3A.2 Install the Expo CLI
npm install -g expo-cli
# or use npx expo without installing globally3A.3 Run prebuild
Expo prebuild reads your app.json and generates the native android/ project:
npx expo prebuild --platform androidThis creates the android/ directory. You only need to run this again if you change app.json, add or remove native packages, or want to regenerate the native project from scratch.
If
android/already exists, prebuild will ask if you want to overwrite it. Answer yes unless you've made manual changes to the native project — manual changes are not preserved through prebuild.
3A.4 Verify the generated project
After prebuild completes:
ls android/
# Should contain: app/, build.gradle, gradlew, settings.gradle, etc.Part 3B: Bare React Native
Skip this if your project is Expo-managed.
3B.1 Verify the android directory
Your project should already have an android/ directory committed to it. Verify:
ls android/
# Should contain: app/, build.gradle, gradlew, settings.gradle, etc.If there is no android/ directory, your project may actually be Expo — check whether "expo" is in your package.json dependencies and follow Part 3A instead.
3B.2 Install native dependencies
If your project uses any packages with native modules, link them:
cd ~/my-app
npm install
# For React Native 0.60+, autolinking handles most packages automatically.
# If any package requires manual linking, its documentation will say so.Part 4: Test Your App
4.1 Open the Android project in Android Studio
npx react-native start --reset-cache &
# In a second terminal:
npx react-native run-androidOr open Android Studio directly:
studio.sh ~/my-app/androidThe first open downloads Gradle dependencies — this takes several minutes. Do not upgrade Gradle if prompted.
4.2 Run on an emulator
In Android Studio:
- Open Device Manager (phone icon with wrench)
- Click Create Virtual Device, pick a Pixel device, and download a system image (API 34+)
- Select the emulator from the device dropdown and click Run (▶)
Or from the command line (once the emulator is running):
npx react-native run-android4.3 Run on a physical device
- Enable Developer Options: Settings > About Phone > tap "Build Number" 7 times
- Enable USB Debugging: Settings > Developer Options > USB Debugging
- Connect via USB and accept the debugging prompt
- Run:
npx react-native run-android4.4 Common issues
Metro bundler not starting — start it manually in a separate terminal: npx react-native start
"SDK location not found" — ANDROID_HOME is not set. Follow step 1.4 and reload your shell.
App crashes immediately — check Logcat in Android Studio (View > Tool Windows > Logcat) for error details.
Red error screen in the app — this is a JavaScript error. Read the message — it usually points directly to the broken file and line.
"Duplicate resources" — clean the build: cd android && ./gradlew clean
Extremely slow emulator — enable KVM hardware acceleration:
Debian/Ubuntu:
sudo apt install qemu-kvm
sudo adduser $USER kvm
# Log out and back inFedora:
sudo dnf install @virtualization
sudo usermod -aG kvm $USER
# Log out and back inPart 5: Prepare for Release
5.1 Set your app icon
Expo projects: add your icon path to app.json and re-run prebuild:
{
"expo": {
"icon": "./assets/icon.png",
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#FFFFFF"
}
}
}
}The icon image must be at least 1024×1024 px. After updating app.json:
npx expo prebuild --platform androidBare React Native: replace the icon files directly in android/app/src/main/res/mipmap-*/. Each density folder needs its own size. Use Android Studio's File > New > Image Asset wizard to generate all sizes from a single 1024×1024 px source.
5.2 Set your splash screen
Expo projects: set in app.json:
{
"expo": {
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#FFFFFF"
}
}
}Then re-run npx expo prebuild --platform android.
5.3 Update your app version
In android/app/build.gradle:
versionCode 1 // Integer, must increase with every Play Store upload
versionName "1.0" // Human-readable string shown to usersFor Expo projects, you can also set these in app.json and they'll be picked up by prebuild:
{
"expo": {
"version": "1.0.0",
"android": {
"versionCode": 1
}
}
}5.4 Configure app permissions
In android/app/src/main/AndroidManifest.xml, inside the <manifest> tag:
<!-- Internet access — required for most apps, verify it's present -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Add only what your app actually uses: -->
<!-- <uses-permission android:name="android.permission.CAMERA" /> -->
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
<!-- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> -->Part 6: Sign and Build the Release Bundle
6.1 Generate a signing key
keytool -genkey -v -keystore ~/my-app-release.keystore \
-alias my-app-key \
-keyalg RSA \
-keysize 2048 \
-validity 10000Back up this keystore file and both passwords. Losing it means you can never update your app on Google Play. Store a copy in a password manager and on external storage.
6.2 Configure Gradle signing
Create android/keystore.properties (do NOT commit this to git — add it to .gitignore):
storeFile=/home/YOUR_USERNAME/my-app-release.keystore
storePassword=your_keystore_password
keyAlias=my-app-key
keyPassword=your_key_passwordEdit android/app/build.gradle — add above the android { block:
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}Inside the android { block, add signing config and update build types:
android {
// ... existing config ...
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}6.3 Build the release bundle
cd ~/my-app/android
./gradlew bundleReleaseYour signed .aab file will be at:
android/app/build/outputs/bundle/release/app-release.aab
If the build fails — read the error output carefully. Common causes: missing SDK components (fix in SDK Manager), Gradle version mismatch (don't upgrade when prompted), or Java version issues (confirm JDK 17 is installed and
JAVA_HOMEpoints to it).
Part 7: Create a Google Play Developer Account
- Go to https://play.google.com/console
- Sign in with your Google account
- Choose Personal or Organization account (Personal is fine for indie developers)
- Pay the one-time $25 USD registration fee
- Complete identity verification
- Wait 24–48 hours for activation
Personal accounts created after November 2023 require a device verification step. Install the Google Play Console mobile app on an Android device, sign in with the same Google account, and follow the verification steps.
Part 8: The Closed Testing Requirement
New personal developer accounts cannot publish directly to production. Google requires:
- Uploading your app to a closed test track
- Having at least 12 testers opted in
- Those testers must remain opted in for 14 consecutive days
- Only then can you apply for production access
8.1 Create your app listing
In Google Play Console, click Create app and work through all required sections:
- Store listing — app description, at least 2 phone screenshots, 1024×500 feature graphic, 512×512 icon, category, contact details
- Content rating — complete the IARC questionnaire honestly
- Target audience — if not specifically for children, do not select children as target
- Privacy policy — required if your app collects any user data; a simple page on GitHub Pages or Notion works
8.2 Set up closed testing and upload your build
- Go to Testing > Closed testing in the left sidebar
- Under Testers, create a list with at least 12 Gmail addresses
- Click Create new release, accept Play App Signing, and upload your
.aabfile - Add release notes and click Start rollout
- Share the generated opt-in link with your testers
Testers must click the link on an Android device, accept the invitation, and install the app. After 14 days with all testers opted in, you can apply for production access from the app Dashboard.
Part 9: Publish to Production
Once you have production access:
- Go to Production > Create new release
- Upload your
.aabfile - Add release notes and select distribution countries
- Click Start rollout to production
First-submission review typically takes a few hours to several days. If approved, your app goes live.
Part 10: Updating Your App
Expo projects:
# 1. Make your code changes
# 2. If you changed app.json or added/removed native packages:
npx expo prebuild --platform android
# 3. Bump versionCode in android/app/build.gradle (must be higher than last upload)
# 4. Build the new bundle
cd android
./gradlew bundleRelease
# 5. Upload the new .aab in Google Play Console
# Production > Create new release > upload > rolloutBare React Native:
# 1. Make your code changes
# 2. Bump versionCode in android/app/build.gradle
# 3. Build the new bundle
cd android
./gradlew bundleRelease
# 4. Upload the new .aab in Google Play ConsoleQuick Reference
# === EXPO: ONE-TIME SETUP ===
npm install
npx expo prebuild --platform android
# === EVERY EXPO CODE CHANGE (JS only — no native changes) ===
# No prebuild needed, just rebuild:
cd android && ./gradlew bundleRelease
# === EVERY EXPO CHANGE THAT TOUCHES NATIVE ===
# (new packages, app.json changes)
npx expo prebuild --platform android
cd android && ./gradlew bundleRelease
# === BARE RN: BUILD ===
cd android && ./gradlew bundleRelease
# === TESTING ===
npx react-native start
npx react-native run-android
# === RELEASE BUNDLE OUTPUT ===
# android/app/build/outputs/bundle/release/app-release.aabTroubleshooting
"SDK location not found" — ANDROID_HOME is not set. Follow step 1.4 and reload your shell.
"JAVA_HOME is not set" — Add to ~/.bashrc:
# Debian/Ubuntu:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
# Fedora:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdkGradle fails with "Could not determine java version" — Multiple Java versions are installed. Make sure JAVA_HOME points specifically to JDK 17.
npx expo prebuild fails — check the error message carefully. Common causes: missing android.package in app.json, an incompatible package version, or a broken package.json. Run npm install first and check for dependency warnings.
"Invariant Violation: requireNativeComponent" or similar — a native module is being used that wasn't installed. Run npm install and then re-run prebuild if using Expo.
App shows blank white screen at startup — the Metro bundler isn't running, or the bundle failed to load. Start Metro (npx react-native start) and check the terminal output for JS errors.
"INSTALL_FAILED_UPDATE_INCOMPATIBLE" — the app is installed with a different signing key. Uninstall the old version from your device before running again.
ProGuard strips a class at runtime (crash after minifyEnabled true) — add a keep rule to android/app/proguard-rules.pro. The crash in Logcat usually names the missing class.
Realistic Expectations
The timeline is longer than you expect. Between the 14-day testing requirement, Google's review process, and debugging, plan for 3–4 weeks from start to live.
AI-generated React Native code often has issues the AI couldn't test. Red screens, missing permissions, and native module errors are common. Budget time for debugging — it's normal.
Google can reject your app. Common reasons: missing privacy policy, misleading store listing, or the app crashing during review. Read Google's developer policies before submitting.
Finding 12 testers is harder than it sounds. Start recruiting before you finish building. Developer communities on Discord and Reddit (r/reactnative, r/androiddev, r/betatesting) organize tester exchanges.