The simplest deployment is a single Cloud Run service that serves both the backend (API + WebSocket) and the frontend (static React build). This is described in Part 1.
If you need to host the frontend separately (e.g., for CDN edge caching, custom domain routing, or independent scaling), see Part 2.
- Google Cloud CLI (
gcloud) installed and initialized - A Gemini API key
- A Google Cloud project with billing enabled
gcloud auth login
gcloud config set project YOUR_PROJECT_IDgcloud services enable \
run.googleapis.com \
cloudbuild.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com \
firestore.googleapis.comFirestore stores session data, transcripts, and reports. No schema or table creation needed — collections are created automatically on first write.
- Go to the Firestore Console
- Click "CREATE DATABASE"
- Select Native mode
- Choose a location (e.g.,
us-central1) - Select Production mode for security rules
# Create the secret
gcloud secrets create GEMINI_API_KEY --replication-policy="automatic"
# Add your API key value
echo -n "YOUR_GEMINI_API_KEY" | gcloud secrets versions add GEMINI_API_KEY --data-file=-
# Grant Cloud Run access to the secret
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:$(gcloud projects describe YOUR_PROJECT_ID --format='value(projectNumber)')-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"The project uses a multi-stage Dockerfile that builds both the TypeScript server and the React client. The runtime image serves the client as static files alongside the API and WebSocket endpoints.
gcloud run deploy glotti \
--source . \
--region us-central1 \
--allow-unauthenticated \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080Once the deploy finishes, gcloud prints the service URL (e.g., https://glotti-abc123yz-uc.a.run.app). Open it in a browser — both the app UI and the backend are served from this single URL.
If you prefer to serve the frontend independently — for example, to use a CDN, a custom domain with a different provider, or to decouple frontend and backend release cycles — you can build and deploy the client separately.
cd client
npm install
npm run buildThis produces a client-dist/ directory containing static files (HTML, JS, CSS) ready for deployment.
The client connects to the backend via a VITE_WS_URL environment variable. When served from the same Cloud Run instance (Part 1), this is not needed — it defaults to the current page's host. For separate deployments, set it before building:
VITE_WS_URL=wss://glotti-abc123yz-uc.a.run.app npm run buildnpm install -g vercel
cd client-dist
vercel --prodOr connect your GitHub repo in the Vercel Dashboard and set:
- Build Command:
cd client && npm run build - Output Directory:
client-dist - Environment Variable:
VITE_WS_URL=wss://YOUR_BACKEND_URL
npm install -g netlify-cli
cd client-dist
netlify deploy --prod --dir=.Or connect your repo in Netlify with the same build settings as Vercel above.
- Go to Cloudflare Pages
- Connect your GitHub repo
- Set Build Command:
cd client && npm run build - Set Build Output Directory:
client-dist - Add environment variable
VITE_WS_URL
- Go to DigitalOcean Apps
- Create a new app from your GitHub repo
- Select Static Site as the component type
- Set Build Command:
cd client && npm run build - Set Output Directory:
client-dist - Add environment variable
VITE_WS_URL
# Create a bucket
gsutil mb -l us-central1 gs://YOUR_BUCKET_NAME
# Enable static website hosting
gsutil web set -m index.html -e index.html gs://YOUR_BUCKET_NAME
# Upload the build
gsutil -m rsync -r client-dist/ gs://YOUR_BUCKET_NAME
# Make publicly readable
gsutil iam ch allUsers:objectViewer gs://YOUR_BUCKET_NAME- Open the frontend URL in your browser
- Select a scenario mode and start a session
- Confirm that WebSocket audio streaming and Gemini Live API barge-ins work
- End the session and verify the report is generated and saved
- Cloud Run Console — select your service
- LOGS tab for real-time server output
- METRICS tab for request counts, latency, and resource usage
- Firestore Console — browse the
sessionscollection
Run the same gcloud run deploy command. Cloud Build will create a new revision automatically.
Cloud Run scales to zero when idle (no cost). To fully remove the service:
- Go to the Cloud Run services list
- Select the service and click DELETE