---
title: "Deploy Your AI App: Web, Your Server, and the Stores"
slug: "deploy-ai-app-web-server-app-stores"
date: "2026-08-24"
tag: "Infrastructure"
summary: "Three documented deployment paths shipped for apps built on ibl.ai: push a Next.js app to platform-hosted deployment using only your ibl.ai API key, containerize the same app for a server your team controls, or generate a Makefile and Fastlane config that submits the Tauri build to the App Store and Google Play."
author: "ibl.ai Engineering"
linkedin: |
  Building the app is the easy part. Shipping it to seven surfaces is where most teams stall.

  We published three deployment paths for apps built on ibl.ai, and they cover the whole distance from "it runs on my laptop" to "it is in the App Store."

  Web, with no third-party account. Zip the app, POST it to the platform hosting endpoint with the API key you already have, poll until the build is READY. No Vercel account, no Vercel token, no CLI. The same project slug redeploys the same URL.

  Your own server. The same app as a container, one image for every environment — no tenant and no backend baked in, so the digest that passed staging is the digest that runs production and repointing is a restart. On-prem, Cloud Run, Kubernetes, air-gapped.

  The app stores. A generated Makefile plus Fastlane config: make ios-release builds the signed .ipa and pushes it to TestFlight, make android-release builds the .aab and pushes it to the Play internal track. We also documented the two things Apple and Google do NOT let you automate, instead of pretending they do.

  And the native side is genuinely one frontend. macOS, Windows, Linux, iOS, and Android are Tauri shells that load your web app, so you deploy once and rebuild each shell against your own domain with your own signing credentials.

  All of it MIT-licensed and public, alongside 73 other skills in the toolkit. Your infrastructure, your certificates, your developer accounts, your users — you own all the code and the data.

  Deploy skill: https://github.com/iblai/vibe/blob/main/adapters/codex/iblai-vibe-ops-deploy.md
  Release skill: https://github.com/iblai/vibe/blob/main/adapters/codex/iblai-vibe-ops-release.md
  Every surface: https://github.com/iblai/os/blob/main/docs/platform-deployment.md

  #iblai #AgenticAI #EnterpriseAI #DevTools #SelfHosted #Tauri
---

**ibl.ai now documents three deployment paths for apps built on the platform: push a Next.js app to platform-hosted deployment with nothing but the API key already in your env file, containerize the same app for a server your team controls, or generate a Makefile and Fastlane config that submits the Tauri build to the App Store and Google Play. On every path, you own all the code and the data.**

All three shipped as public, MIT-licensed files: two agent skills in [iblai/vibe](https://github.com/iblai/vibe) — [`iblai-vibe-ops-deploy`](https://github.com/iblai/vibe/blob/main/adapters/codex/iblai-vibe-ops-deploy.md) and [`iblai-vibe-ops-release`](https://github.com/iblai/vibe/blob/main/adapters/codex/iblai-vibe-ops-release.md) — and a [platform deployment guide](https://github.com/iblai/os/blob/main/docs/platform-deployment.md) in [iblai/os](https://github.com/iblai/os) covering every surface the client ships on.

## What is the mental model for deploying across seven surfaces?

There is **one** frontend — the Next.js web app. Every native app is a thin native shell that loads it in a webview. That collapses seven deployment problems into two configuration decisions.

<table style="width:100%; border-collapse:collapse; margin:1.5rem 0; font-size:0.95rem;">
  <thead>
    <tr style="background:#f5f5f0; border-bottom:2px solid #2175C5;">
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Layer</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">What it is</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">How you point it at your backend</th>
    </tr>
  </thead>
  <tbody>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Web app</strong></td>
      <td style="padding:0.75rem;">The Next.js SPA — also what you self-host at your own domain.</td>
      <td style="padding:0.75rem;"><code>NEXT_PUBLIC_*</code> at build time, or at container start via <code>window.__ENV__</code>.</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Native shells</strong></td>
      <td style="padding:0.75rem;">Tauri wrappers for macOS, Windows, Linux, iOS, and Android that load that web app.</td>
      <td style="padding:0.75rem;"><code>TAURI_DEV_URL</code>, compile-time, set to your web app's URL.</td>
    </tr>
  </tbody>
</table>

So the recipe for every native platform is identical: deploy the web app at your domain first, then build each shell with `TAURI_DEV_URL=https://your-app.example.com`.

<a href="/images/agentic-os/os-chat.webp" target="_blank" rel="nofollow noopener noreferrer"><img src="/images/agentic-os/os-chat.webp" alt="The ibl.ai agent chat interface — the single Next.js frontend that the macOS, Windows, Linux, iOS, and Android shells each load in a webview." width="1600" height="1013" /></a>

## How do you deploy an ibl.ai app to the web without a third-party account?

The platform holds the hosting credential for your tenant, so the deploy needs only the `DOMAIN`, `PLATFORM`, and `TOKEN` values already sitting in `iblai.env`. No Vercel account, no Vercel token, no `vercel` CLI.

The flow is three steps: zip the app, `POST` it to the tenant's hosting endpoint, poll until the build reports `READY`. Static exports deploy in seconds; server builds take a few minutes, and the skill gives up after roughly 10 minutes rather than hanging.

```bash
RESP=$(curl -s -X POST "$BASE/providers/vercel/hosting/deployment/" -H "$AUTH" \
  -F "file=@app.zip" -F "project=$PROJECT" -F "framework=$MODE")
```

Three limits are worth knowing before you build the zip: **50 MB compressed, 2,000 files, 200 MB uncompressed.** The platform strips `.git/`, `node_modules/`, `__MACOSX/`, `.DS_Store`, and `iblai.env` from the upload, but keeping them out keeps you inside the caps.

Two details in the skill exist because guessing them breaks deploys.

The live URL is **read from the deployment's alias list, never derived from the project name** — long names get truncated and collisions get a hash suffix. And `push_state` must reach `pushed` before `ready_state` is read, or a stale `READY` from the previous deployment masks a failure that already happened.

Redeploying is the same `POST` with the same `project` slug, which is why the slug is kept stable: same slug, same project, same URL.

## How do you deploy the same app to a server your team controls?

The same app, containerized. That is the path when the words *on-prem*, *self-host*, *our own VM*, *Kubernetes*, or *air-gapped* appear — or when the project already carries a `Dockerfile`, in which case you deploy the way the project already deploys.

```bash
docker build -t <app> .
docker run -d -p 8080:8080 \
  -e NEXT_PUBLIC_IBL_PLATFORM=<tenant> \
  -e NEXT_PUBLIC_API_BASE_URL=https://api.iblai.app \
  <app>
```

Two rules decide whether that deploy survives contact with a second environment.

**One image, every environment.** No tenant, no backend, and no customer baked into the artifact. The digest that passed staging is the digest that runs production, and repointing it is a restart rather than a rebuild.

**Reproduce the SSO callback route.** Sign-in leaves for the login SPA and returns to `<origin>/sso-login-complete`. A static server that does not map that path 404s the callback, and login dies with no useful error.

Next.js routing handles it; nginx, S3, and GitHub Pages do not until told.

There is a third requirement that is easy to miss because it lives on the platform rather than in the deploy: **the tenant has to know the new origin.**

Add the deployment's origin to the tenant's allowed redirect origins, or sign-in leaves and never comes back — and the symptom looks like a broken app, not a missing setting.

If the choice between hosted and self-hosted is a compliance or data-residency decision, that is a decision for the institution, not the deploy script. The options are laid out on [hosted deployment](/hosted-deployment), [on-premise deployment](/on-premise-deployment), and the [full code license](/full-code-license).

## How do you submit a Tauri app to the App Store and Google Play?

The release skill generates a `Makefile` and a `fastlane/` config, so a single command builds the app and submits it. Tauri produces the `.ipa` and `.aab`; Fastlane handles the app record and the upload.

```bash
make ios-release        # build .ipa  -> upload to TestFlight
make android-release    # build .aab  -> upload to Play internal track
make release-all        # both
```

`make doctor` checks the toolchain and credentials before anything else runs, and `make bump VERSION=1.2.3` rewrites the version in `tauri.conf.json`.

Submissions land in **TestFlight** on iOS and the **internal track** on Android, as a draft — promoting to public review stays a deliberate act in the consoles.

The prerequisites are real money and real accounts: an **Apple Developer Program membership at $99/year** and a **Google Play Developer account at $25 one-time**, plus the App Store Connect API key, the Play service-account JSON, and an Android release keystore.

The skill also documents **the two things the platforms do not let you automate**, rather than pretending otherwise.

Google Play cannot create the app listing or accept the *first* `.aab` through the API — both are manual, once, in the Play Console, after which Fastlane `supply` handles every release.

And creating the App Store Connect record sometimes requires an Apple-ID session login with 2FA even when the API key is valid, so `make ios-create` tries the key first and tells you when Apple refuses.

## Which surfaces are automated today, and which still need a human?

Seven surfaces ship from the same frontend, and they are not equally automated. This is the honest state of it.

<table style="width:100%; border-collapse:collapse; margin:1.5rem 0; font-size:0.95rem;">
  <thead>
    <tr style="background:#f5f5f0; border-bottom:2px solid #2175C5;">
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Surface</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Point it at your backend</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Build</th>
      <th style="text-align:left; padding:0.75rem; color:#5f6368;">Release path</th>
    </tr>
  </thead>
  <tbody>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Web</strong></td>
      <td style="padding:0.75rem;"><code>NEXT_PUBLIC_*</code> (build or runtime)</td>
      <td style="padding:0.75rem;"><code>pnpm build</code> / Docker</td>
      <td style="padding:0.75rem;">CI to a Docker image</td>
    </tr>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>macOS</strong></td>
      <td style="padding:0.75rem;"><code>TAURI_DEV_URL</code></td>
      <td style="padding:0.75rem;"><code>pnpm tauri:build:devid</code> / <code>:mas</code></td>
      <td style="padding:0.75rem;">CI to a signed, notarized DMG</td>
    </tr>
    <tr style="background:#f0f9ff; border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Windows / Surface</strong></td>
      <td style="padding:0.75rem;"><code>TAURI_DEV_URL</code></td>
      <td style="padding:0.75rem;"><code>pnpm tauri:build</code></td>
      <td style="padding:0.75rem;">CI to signed NSIS, x64 and ARM64</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Linux</strong></td>
      <td style="padding:0.75rem;"><code>TAURI_DEV_URL</code></td>
      <td style="padding:0.75rem;"><code>cargo tauri build</code></td>
      <td style="padding:0.75rem;">Build from source</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>iOS</strong></td>
      <td style="padding:0.75rem;"><code>TAURI_DEV_URL</code> + Team ID</td>
      <td style="padding:0.75rem;"><code>make tauri-ios-build</code></td>
      <td style="padding:0.75rem;">Manual Xcode to App Store</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Android</strong></td>
      <td style="padding:0.75rem;"><code>TAURI_DEV_URL</code> + keystore</td>
      <td style="padding:0.75rem;"><code>make tauri-android-build-aab</code></td>
      <td style="padding:0.75rem;">Manual to Play Console</td>
    </tr>
    <tr style="border-bottom:1px solid #e5e7eb;">
      <td style="padding:0.75rem;"><strong>Chrome extension</strong></td>
      <td style="padding:0.75rem;"><code>panel.html</code> app URL</td>
      <td style="padding:0.75rem;">Manifest version bump</td>
      <td style="padding:0.75rem;">CI, with store secrets configured</td>
    </tr>
  </tbody>
</table>

Three of the seven still end at a console a human logs into. That is a platform constraint rather than a missing feature, and the release skill is built around it instead of over it.

## What changes when you own the deployment instead of renting it?

The reason these three paths matter together is that none of them puts a vendor between you and your users. The hosting credential belongs to your tenant. The container runs on your metal. The App Store listing is yours, signed with your certificates, under your developer account.

That is the same story as the rest of the platform: [Agentic Vibe](/product/agentic-vibe) scaffolds the app, [Agentic OS](/product/agentic-os) runs the agents behind it, it is model-agnostic across any LLM with no per-seat pricing, and you own all the code and the data.

ibl.ai is family-owned and operated from New York, NY — a U.S.-headquartered, domestically-owned long-term partner, not a vendor that sells licenses and moves on.

The deploy and release skills are two of **75 in [iblai/vibe](https://github.com/iblai/vibe)**, all MIT-licensed, with adapters generated for Codex and Cursor alongside the native Claude Code skills.

Scaffolding an app is covered in [App CLI: Scaffold Production-Ready AI Applications](/updates/app-cli-frontend-scaffolding); the native desktop build is covered in [The ibl.ai Mac App](/updates/mac-app-offline-llms-sandboxed-agents), and the shipped clients are on the [download page](/download).
