Couldn't load this page.

← ブログ
Engineering

Lizard Sandboxesを使って1日でLovableクローンを構築する

Yura Oak

Lovableクローンには、生成したコードを実行する場所が必要です。モデルはファイルを書き込み、依存関係をインストールし、ビルドを確認して、プレビューサーバーを起動します。顧客のプロジェクトごとに独自のワークスペースが必要になります。

Lizard Sandboxesを使用すると、バックエンドはLizard SDKを通じてそのワークスペースを作成し、ファイルを送信してコマンドを実行し、プライベートプレビューのURLを返します。サーバーを購入したり、マシンのフリートを予約したりすることなく開始できます。

ReactとNode.jsの知識があり、アプリのテンプレートから始める開発者にとって、小規模なMVPの目標として1日は妥当な期間です。プロンプトを入力し、Reactアプリを生成してプレビューを開き、変更を要求する機能を作成します。決済、チームアクセス、完全なデプロイメント製品の構築にはさらに作業が必要です。

Lizardでのテスト内容

2026年10月15日、Lizard SDK 0.1.69を使用して、us-east-1のSmall Firecracker Sandboxesで実行ワークフローをテストしました。

確認項目結果
依存関係のインストールとReactアプリのビルド合格
ブラウザでプライベートHTTPSプレビューを開く合格
Reactのボタンでサンプルメンバーをフィルタリングする合格
アクセストークンなしでプレビューをリクエストするHTTP 401
ソースファイルを変更し、再ビルドしてプレビューをリロードする合格
編集したソースを保存し、新しいサンドボックスで再ビルドする合格

最初の成功した実行では、サンドボックスの作成から動作するプレビューの受信まで約22秒かかりました。これには、Node.jsとnpmのインストール、スターターのアップロード、依存関係のインストール、アプリのビルドが含まれます。これは1回のスモークテストであり、起動のベンチマークではありません。

このテストでは、最初のドラフトで2つのエラーも発見されました。サンドボックスオブジェクトは、そのIDをsandbox.sandboxIdとして公開します。また、受け取ったinterpreterサンドボックスにはNode.jsやnpmが含まれていませんでした。以下のコードは、ビルドを実行する前に固定バージョンのNode.jsをインストールします。

実行とプレビューのワークフローをテストしました。以下の1日のスケジュールは構築計画です。完全なSaaS製品の作成時間を計測したり、外部モデルの出力品質をテストしたりはしていません。

最初のバージョンは小さく保つ

1つのReactとViteのテンプレートから始めます。ユーザーがサンプルデータを使ってシンプルなダッシュボード、ランディングページ、または社内ツールを作成できるようにします。

最初のリリースには、サインイン、プロジェクトリスト、チャットボックス、ビルド出力、および動作するプレビューが必要です。ユーザーが後で戻れるようにソースファイルを保存します。

依存関係のリストは固定しておきます。モデルに既知のパッケージセットを提供し、いくつかのソースファイルを編集させます。これにより、ビルドが失敗した際の修正が容易になります。

最初の有用なリクエストは次のとおりです。

Build a dashboard for a small gym. Show monthly revenue,
active members, and upcoming classes. Use sample data.

プレビューが機能したら、メンバーシップフィルターの追加または変更を行うフォローアップリクエストを送信します。これにより、テストのための具体的な手順が得られます。

バックエンドで制御を管理する

バックエンドは、モデルの接続、LizardのAPIキー、およびユーザーとプロジェクトのマッピングを管理します。

User prompt
    |
Your backend: sign-in, project access, model calls
    |
Lizard SDK
    |
Sandbox: source files, dependencies, build, preview
    |
Private preview URL returned to the project owner

所有者、サンドボックスID、会話、および保存されたファイルのバージョンをデータベースに保存します。ログの読み取りやプレビューを開くなど、すべての操作の前に所有権を確認してください。

プラットフォームの認証情報はバックエンドに保持します。生成されたコードには、LizardのAPIキーやモデルプロバイダーのキーは必要ありません。

最初のバージョンでは、appTsxとappCssを含む構造化された出力を返すようにモデルに要求します。両方の文字列を検証し、レスポンスのサイズ制限を設定します。バックエンドはそれらを固定パスであるsrc/App.tsxとsrc/App.cssに書き込みます。パッケージファイル、ロックファイル、ビルドスクリプトはバックエンドが制御します。

スターターがサポートするインポートをモデルに伝えます。ビルドが失敗した場合は、関連するエラー出力を返し、最大2回の修復試行を許可します。その後、ユーザーにエラーを表示します。

サンドボックスを作成してプレビューを開く

Node.jsのバックエンドにテスト済みのSDKバージョンをインストールします。

npm install @lizard-build/sdk@0.1.69

ビルダーが使用するLizardプロジェクトに限定されたキーを作成します。

lizard keys create lovable-builder --project YOUR_PROJECT_ID --json

バックエンド環境でLIZARD_API_KEYとLIZARD_PROJECT_IDを設定します。SDKはLizard CLIのログイン情報を読み取ったり、単独で.envファイルを読み込んだりしません。

ロックファイルと動作するnpm run buildスクリプトを備えた、信頼できるReactとViteのスターターを準備します。今回のテストでは、React 19.2.0、Vite 7.1.12、TypeScript 5.9.3、Python 3.11.16を使用しました。独自のスターターでは依存関係を最新に保ち、更新時に再テストしてください。

以下の関数は、そのスターターを相対ファイルパスからファイル内容へのマップとして受け取ります。自身で制御するファイルから読み込んでください。モデルや認証されていないリクエストからstarterFilesを受け入れないでください。

セットアップ手順は、このテストで使用されたLinux x64サンドボックスを対象としています。nodejs.orgからNode.js 22.23.3をダウンロードし、リリースのチェックサムとアーカイブを照合します。生成されたコードにワークスペースを渡す前にセットアップを実行してください。

import { Lizard } from '@lizard-build/sdk'
import { setTimeout as delay } from 'node:timers/promises'

const project = process.env.LIZARD_PROJECT_ID
if (!project) throw new Error('LIZARD_PROJECT_ID is required')

const lizard = new Lizard({ project })

export async function createPreview(
  starterFiles: Record<string, string>,
  appTsx: string,
  appCss: string,
) {
  const sandbox = await lizard.create('interpreter', {
    size: 'small',
    timeoutMs: 10 * 60 * 1000,
    metadata: { purpose: 'lovable-article-smoke-20261015' },
  })

  try {
    const info = await sandbox.getInfo()
    if (info.runtime !== 'firecracker') {
      throw new Error('This workflow requires Firecracker')
    }

    const setup = await sandbox.process.exec(['/bin/bash', '-lc', `
set -euo pipefail
version=v22.23.3
archive=node-$version-linux-x64.tar.xz
if [ "$(node --version 2>/dev/null || true)" != "$version" ] || ! command -v npm >/dev/null; then
  cd /tmp
  curl -fsSLO "https://nodejs.org/dist/$version/$archive"
  curl -fsSL "https://nodejs.org/dist/$version/SHASUMS256.txt" | grep "  $archive$" | sha256sum -c -
  tar -xJf "$archive" -C /usr/local --strip-components=1
fi
node --version
npm --version
python --version
`], { timeoutMs: 120_000 })
    if (setup.exitCode !== 0) throw new Error(setup.stderr || setup.stdout)

    for (const [path, content] of Object.entries(starterFiles)) {
      await sandbox.fs.write(`/workspace/app/${path}`, content)
    }
    await sandbox.fs.write('/workspace/app/src/App.tsx', appTsx)
    await sandbox.fs.write('/workspace/app/src/App.css', appCss)

    for (const command of [['npm', 'ci'], ['npm', 'run', 'build']]) {
      const result = await sandbox.process.exec(command, {
        workdir: '/workspace/app',
        timeoutMs: 120_000,
      })
      if (result.exitCode !== 0) {
        throw new Error(result.stderr || result.stdout)
      }
    }

    const server = await sandbox.process.exec(
      ['/bin/bash', '-lc', 'nohup python -m http.server 3000 --bind 0.0.0.0 ' +
      '--directory dist > /tmp/preview.log 2>&1 < /dev/null &'],
      { workdir: '/workspace/app' },
    )
    if (server.exitCode !== 0) throw new Error(server.stderr)

    const route = await sandbox.exposePort(3000)
    for (let attempt = 0; attempt < 15; attempt++) {
      const response = await fetch(`https://${route.hostname}`, {
        headers: { 'X-Lizard-Access-Token': route.accessToken },
        signal: AbortSignal.timeout(3_000),
      }).catch(() => null)

      const ready = response?.ok
      await response?.body?.cancel()
      if (ready) {
        return { sandboxId: sandbox.sandboxId, previewUrl: route.url }
      }
      await delay(500)
    }
    throw new Error('Preview did not become ready')
  } catch (error) {
    await sandbox.kill()
    throw error
  }
}

モデルのレスポンスを検証した後にcreatePreview(starterFiles, result.appTsx, result.appCss)を呼び出します。返されたsandboxIdをユーザーのプロジェクトに関連付けて保存し、アクセスを確認した後にのみpreviewUrlを返します。

URLにはアクセストークンが含まれています。公開ログや分析データには含めないでください。これはサンドボックスの公開ポートへのアクセスを許可するため、認証情報として扱います。

この例では、ビルドされたフロントエンドをPythonのHTTPサーバーで提供します。これをプライベートな開発プレビューに使用します。初期アプリは1つのルートに保持するか、ハッシュルーティングを使用してください。本番環境のホスティングは別のデプロイメントにする必要があります。

Lizard SDKリファレンスでは、コマンド、ファイル、プライベートポート、およびサンドボックスのライフサイクル操作について説明しています。

フォローアップの変更と保存されたプロジェクトの処理

lizard.connect(sandboxId)を通じてプロジェクトのサンドボックスに再接続します。承認されたソースファイルを読み取り、ユーザーの次のリクエストとともにモデルに送信し、レスポンスを検証して変更を書き込み、再度ビルドを実行します。

テストでは見出しを変更し、サンプルメンバーを追加しました。再ビルドして同じプレビューURLをリロードすると、ブラウザに新しいバージョンが表示され、フィルターが機能しました。

ビルドが成功するたびにソースファイルを保存します。プライベートバケットを使用してソースバンドルをManaged Object Storageに保存し、プロジェクトの記録をManaged Postgresに保持します。これらのバックエンドサービスは、プレビューワークスペースとは別に実行されます。

サンドボックスの期限が切れた後に戻ってきたユーザーには、新しいサンドボックスが必要です。保存されたソースとスターターを復元し、再ビルドします。編集したソースを2つ目のサンドボックスに読み込み、結果のアプリをブラウザで開くことでこれを確認しました。

Persistent Volumesは、ワークスペースのファイルを保持するためのもう1つのオプションです。各ボリュームは一度に1つのサンドボックスにアタッチされます。ストレージは1つのノード上に存在するため、重要なファイルの外部コピーを保持してください。サンドボックスストレージの詳細

Firecrackerで生成されたコードを分離する

Lizard Sandboxesは、独自のカーネルを持つFirecracker microVMをサポートしています。この例では、依存関係をインストールしたりアプリを実行したりする前にランタイムを確認します。

古いボリュームやスナップショットではコンテナランタイムを使用できます。製品にFirecrackerが必要な場合は、復元やボリュームのサポートを追加する際にもランタイムの確認を維持してください。ランタイムの詳細

アクティブな顧客プロジェクトごとに個別のサンドボックスを作成します。本番データベースの認証情報やプラットフォームキーは、そのワークスペースの外部に保持してください。サンドボックスはネットワークリクエストを行うことができるため、生成されたコードは内部に置かれたシークレットを外部に送信する可能性があります。

メインアプリとは別のブラウザオリジンでプレビューを提供します。MVPの場合、新しいタブでプライベートプレビューを開くことで統合をシンプルに保てます。後でプレビューを埋め込む場合は、iframeの権限を確認し、メッセージを検証してください。

ユーザーを招待する前に、2つのアカウントでテストします。プロジェクトIDを変更しても、一方のアカウントが他方のファイル、ログ、またはプレビューURLを読み取れないようにする必要があります。Firecrackerは実行の境界を処理し、アプリケーションはユーザーアクセスを処理します。

Lovableクローンの実行コストは?

Smallは、2 vCPU、4 GBのRAM、および5 GBのローカルディスクで1時間あたり$0.0162かかります。Lizardは実行時間を秒単位で請求します。

1セッションあたり10分の場合:

セッション数サンドボックスのコンピューティング
1$0.0027
100$0.27
1,000$2.70
10,000$27.00

これらはコンピューティングの概算です。Lizard Proは月額$19で、$19分の利用枠が含まれています。それを超える利用分はpay as you goとなります。7日間のトライアルには$5分の利用枠が含まれており、クレジットカードが必要です。価格は2026年10月15日に確認したものです。現在の料金

モデルのトークン、バックエンドのホスティング、データベース、および永続ストレージも合計コストに影響します。独自の価格を設定する前に、プロジェクトごとのモデル使用量とサンドボックスの時間を追跡してください。

ユーザーを見つける前にサーバーを購入したり容量を予約したりする必要はありません。ただし、アイドル時間の実行にもコストがかかります。使用していないセッションは一時停止または終了してください。保持されたストレージにも料金が発生する場合があります。

この例では10分の有効期限を設定しています。コマンドを実行してもその期限は延長されません。バックエンドにアイドルセッションのポリシーと停止ボタンを追加し、ソースを保存してセッションが終了したときにsandbox.kill()を呼び出します。ライフサイクルリファレンス

1日のMVP計画

このスケジュールは、認証機能を備えたWebアプリのスターターがすでにあることを前提としています。

時間成果物
1時間目プロジェクトページ、プロンプト入力、および所有権の確認
2〜3時間目モデルレスポンスの検証、サンドボックスの作成、ファイルの書き込み、およびビルド
4時間目プライベートプレビューと表示可能なビルドエラー
5時間目フォローアップの編集と保存されたプロジェクトバージョン
6時間目セッションの有効期限、再試行の制限、およびユーザーごとのクォータ
7時間目2つのアカウントといくつかの異なるプロンプトでのテスト
8時間目ビルダーをデプロイし、小規模なテストグループを招待する

テスターを招待する前に、アプリの作成、変更のリクエスト、セッションの終了、保存されたプロジェクトの再開など、ユーザーの全体的なジャーニーを確認します。ビルドが失敗した場合は、有用なエラーを生成し、そのサンドボックスを解放する必要があります。

1つのテンプレートと動作するプレビューから始めます。Lizard SandboxesとLizard SDKは、その最初のリリースに必要な実行、ファイル、およびライフサイクルの制御を提供します。

AI で構築。Lizard で公開。

本番公開にプラットフォームチームは必要ありません。クラウド全体が、1 つの CLI コマンドですぐ使えます。

無料で試す
サービス
—
アドオン
—
デプロイ
—
Sandboxes の起動数
—

当サイトでは、基本機能と分析のために Cookie を使用しています。詳しくはCookie ポリシーをご覧ください。