Vibe code to app stores

Android on Linux

11 min read

A step-by-step guide for shipping a vibe-coded web app to Google Play from a Linux machine — covering tool installation (Debian/Ubuntu and Fedora/RHEL), Capacitor setup, 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 web app project (ZIP from Bolt.new, Lovable, etc.)
  • About 2–4 hours for initial setup, then ~3 weeks for the publishing process

Part 1: Install the Required Tools

You need four things: Node.js, Java (JDK), Android Studio, and Capacitor (installed in Part 3).

1.1 Install baseline utilities

Debian/Ubuntu:

sudo apt update
sudo apt install -y curl wget unzip git

Fedora:

sudo dnf install -y curl wget unzip git

1.2 Install Node.js (v22+)

Capacitor 8 requires Node.js 22 or newer.

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 --version

Option 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 --version

Fedora:

curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
sudo dnf install -y nodejs
node --version

1.3 Install Java (JDK 17)

Debian/Ubuntu:

sudo apt update
sudo apt install -y openjdk-17-jdk
java -version

Fedora:

sudo dnf install -y java-17-openjdk-devel
java -version

1.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 wget

Fedora (64-bit):

sudo dnf install -y zlib.i686 ncurses-libs.i686 bzip2-libs.i686 \
  libstdc++.i686 libX11 libXext libXrender libXtst \
  fontconfig freetype unzip wget

Step 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.sh

Step 3: First-time setup

When Android Studio launches:

  1. Choose "Do not import settings"
  2. Select "Standard" installation type
  3. 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 --version

1.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;
EOF

Part 2: Export and Prepare Your Web App

2.1 Export from your vibe coding tool

In Bolt.new: click the project title > Export > Download. Other tools have similar export options. Unzip the downloaded file:

mkdir ~/my-app
cd ~/my-app
unzip ~/Downloads/your-project.zip -d .

2.2 Identify your build output directory

Capacitor needs to know where your built app ends up. Common output directories:

FrameworkBuild commandOutput folder
Vite (React/Vue/Svelte)npm run builddist
Create React Appnpm run buildbuild
Angularng builddist/your-app
Next.js (static export)next buildout

2.3 Make sure your app builds

cd ~/my-app
npm install
npm run build

Common issues with vibe-coded exports:

  • Missing .env variables — create a .env file with the needed API keys
  • Dependency conflicts — delete node_modules and package-lock.json, then re-run npm install
  • TypeScript errors — fix the specific file and line the error points to

Note: If your app has a backend (API routes, database), Capacitor wraps the frontend only. Your backend still needs to be hosted separately (Supabase, Railway, etc.) and your app must point to that hosted URL.


Part 3: Add Capacitor to Your Project

3.1 Install Capacitor

cd ~/my-app
npm install @capacitor/core @capacitor/cli

3.2 Initialize Capacitor

npx cap init

You'll be prompted for:

  • App name — the name users see under the icon
  • App Package ID — a unique reverse-domain identifier like com.yourname.myapp. This is permanent and cannot be changed after publishing.
  • Web asset directory — the build output folder from Part 2 (usually dist or build)

Verify the generated capacitor.config.ts:

import type { CapacitorConfig } from '@capacitor/cli';
 
const config: CapacitorConfig = {
  appId: 'com.yourname.myapp',
  appName: 'My App',
  webDir: 'dist'   // Must match your actual build output folder
};
 
export default config;

Critical: webDir must point to the folder containing your built index.html. A wrong value produces a blank screen.

3.3 Add the Android platform

npm install @capacitor/android
npm run build
npx cap add android

This creates an android/ folder — a complete Android Studio project.

3.4 Sync web code to the Android project

Run this every time you change your web code:

npm run build
npx cap sync

Part 4: Test Your App

4.1 Open in Android Studio

npx cap open android

The first open downloads Gradle dependencies — this takes several minutes. Do not upgrade Gradle if prompted.

4.2 Run on an emulator

In Android Studio:

  1. Open Device Manager (phone icon with wrench)
  2. Click Create Virtual Device, pick a Pixel device, and download a system image (API 34+)
  3. Select the emulator from the device dropdown and click Run (▶)

4.3 Run on a physical device

  1. Enable Developer Options: Settings > About Phone > tap "Build Number" 7 times
  2. Enable USB Debugging: Settings > Developer Options > USB Debugging
  3. Connect via USB and accept the debugging prompt
  4. Select your device in Android Studio and click Run

4.4 Common issues

Blank white screenwebDir is wrong or you forgot npm run build before npx cap sync.

App crashes immediately — check Logcat (View > Tool Windows > Logcat) for errors.

API calls failinglocalhost in the emulator refers to the emulator itself. Use 10.0.2.2 to reach your development machine's localhost, or use your deployed backend URL.

Extremely slow emulator — enable KVM hardware acceleration:

Debian/Ubuntu:

sudo apt install qemu-kvm
sudo adduser $USER kvm
# Log out and back in

Fedora:

sudo dnf install @virtualization
sudo usermod -aG kvm $USER
# Log out and back in

Part 5: Prepare for Release

5.1 Set your app icon and splash screen

npm install @capacitor/assets --save-dev

Create an assets/ folder at the project root and add:

  • icon-only.png — at least 1024×1024 px, square, no transparency
  • icon-foreground.png — foreground layer for adaptive icons (1024×1024 px)
  • icon-background.png — background layer for adaptive icons (1024×1024 px)
  • splash.png — at least 2732×2732 px
  • splash-dark.png — dark mode splash (same size)

Generate all Android icon and splash variants:

npx capacitor-assets generate

5.2 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 users

5.3 Configure app permissions

In android/app/src/main/AndroidManifest.xml, inside the <manifest> tag:

<!-- Internet access (included by default in Capacitor, but verify) -->
<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" /> -->

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 10000

Back 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_password

Edit 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 false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

6.3 Build the release bundle

cd ~/my-app
npm run build
npx cap sync
 
cd android
./gradlew bundleRelease

Your 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_HOME points to it).


Part 7: Create a Google Play Developer Account

  1. Go to https://play.google.com/console
  2. Sign in with your Google account
  3. Choose Personal or Organization account (Personal is fine for indie developers)
  4. Pay the one-time $25 USD registration fee
  5. Complete identity verification
  6. 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:

  1. Uploading your app to a closed test track
  2. Having at least 12 testers opted in
  3. Those testers must remain opted in for 14 consecutive days
  4. 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

  1. Go to Testing > Closed testing in the left sidebar
  2. Under Testers, create a list with at least 12 Gmail addresses
  3. Click Create new release, accept Play App Signing, and upload your .aab file
  4. Add release notes and click Start rollout
  5. 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:

  1. Go to Production > Create new release
  2. Upload your .aab file
  3. Add release notes and select distribution countries
  4. 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

# 1. Make your code changes
# 2. Rebuild
npm run build
npx cap sync
 
# 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 > rollout

Quick Reference

# === ONE-TIME SETUP ===
npm install @capacitor/core @capacitor/cli
npx cap init
npm install @capacitor/android
npx cap add android
 
# === EVERY CODE CHANGE ===
npm run build
npx cap sync
 
# === TESTING ===
npx cap open android
npx cap run android
 
# === RELEASE BUILD ===
cd android && ./gradlew bundleRelease
# Output: android/app/build/outputs/bundle/release/app-release.aab

Troubleshooting

"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-openjdk

Gradle fails with "Could not determine java version" — Multiple Java versions are installed. Make sure JAVA_HOME points specifically to JDK 17.

"capacitor.config.ts: webDir does not exist" — Run npm run build first.

App shows blank screen — Verify webDir matches the actual build output folder and that index.html is inside it.

"INSTALL_FAILED_UPDATE_INCOMPATIBLE" — Uninstall the old version from your device before running again.


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.

Capacitor wraps, it doesn't transform. Your app behaves exactly like it does in a mobile browser. Test in Chrome's mobile emulation mode and fix layout issues before wrapping.

Performance won't match a true native app. Animations may be janky on older phones, background tasks are limited, and push notifications require extra setup. Capacitor plugins can extend native functionality, but there are limits.

Google can reject your app. Common reasons: missing privacy policy, misleading store listing, or the app being deemed too simple. 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/androiddev, r/betatesting) organize tester exchanges.