Deploy project previews to Prevu Cloud. Use when asked to preview, deploy, demo, or share a project via a temporary URL. Supports static sites (HTML/React/Vue/Next.js), backend services (Python/Node/Go), Docker images, and fullstack apps with infrastructure dependencies (PostgreSQL, Redis, MinIO). Triggers on "deploy preview", "prevu", "preview this", "share a demo", "deploy to preview", "give me a URL".
Deploy projects to Prevu Cloud for instant preview URLs.
Key rule: Build locally, upload artifacts. Prevu runs your compiled output — it does not build your project.
The @prevu/mcp-server package must be configured. If not available, use the HTTP API directly.
npx @prevu/mcp-server
Or configure in your MCP client:
{ "mcpServers": { "prevu": { "command": "npx", "args": ["-y", "@prevu/mcp-server"] } } }
⚠️ Two-Phase Deploy (IMPORTANT): Some static site generators (Hugo, Jekyll, etc.) bake the site URL into the HTML at build time (e.g., Hugo's baseURL). Since the preview subdomain is only known after creation, you must use a two-phase flow:
Phase 1 — Create preview first (before building):
create_preview(mode="static", name="my-site", ttl_seconds=3600)
# → returns subdomain, e.g. "abc123"
# → preview URL: https://abc123.prevu.page
Phase 2 — Build with the actual URL, then upload:
# Hugo example:
hugo --minify -b "https://abc123.prevu.page/"
# Then zip and upload the output directory
For frameworks that don't bake URLs into HTML (plain HTML, most React SPAs with relative paths), you can skip phase 1 and use the simple flow:
create_preview(mode="static", path="./dist")
Next.js requires output: 'export' in next.config.mjs for static export.
Go: Cross-compile to Linux binary locally, then deploy:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server .
create_preview(mode="runtime", runtime="go", path="./output", start_command="./server", runtime_port=8080, healthcheck_path="/health")
Python: Include requirements.txt. Dependencies are auto-installed via pip install --target in an init container:
create_preview(mode="runtime", runtime="python:3.12", path="./src", start_command="python app.py", runtime_port=8080, healthcheck_path="/health")
Node.js: Build locally first. Upload only the build output (e.g., .next/standalone), NOT the full source tree. Do NOT include node_modules/ — dependencies are auto-installed via npm install on the server. If node_modules/ is included, platform-specific binaries may be incompatible (e.g., macOS → Linux):
create_preview(mode="runtime", runtime="node:22", path="./deploy", start_command="node server.js", runtime_port=3000, healthcheck_path="/health")
create_preview(mode="image", image_ref="ghcr.io/user/app:latest", runtime_port=3000, healthcheck_path="/health")
Prevu auto-provisions databases and services. Add dependencies to create_preview:
{
"dependencies": [
{
"type": "postgres",
"mode": "dsn",
"env_mapping": { "DSN": "DATABASE_URL" }
}
]
}
Available dependency types:
| Type | Modes | Canonical Keys |
|---|---|---|
postgres | dsn → [DSN]; individual → [HOST, PORT, USER, PASSWORD, DATABASE, SSLMODE] | Map to your env vars |
redis | url → [URL, PREFIX]; individual → [HOST, PORT, PASSWORD, PREFIX] | |
minio | individual → [ENDPOINT, ACCESS_KEY, SECRET_KEY, BUCKET, USE_SSL, REGION]; url → [URL] |
PostgreSQL options: {"extensions": "uuid-ossp,pgcrypto"}.
Connection info is injected as environment variables into the preview container.
Deploy as two separate previews:
NEXT_PUBLIC_API_URL=https://xxx.prevu.page npm run build)The backend must handle CORS if frontend and backend are on different subdomains.
create_preview(...) → returns preview_id, url, claim_url, token
get_preview_status(id) → check deployment progress
destroy_preview(id, token) → cleanup (also destroys provisioned dependencies)
Unclaimed previews expire in 20 minutes. The ttl_seconds parameter only takes effect after claiming.
When you create a preview, always show the claim URL to the user. This is the most important output.
| State | TTL | Behavior |
|---|---|---|
| Unclaimed | 20 minutes | Preview auto-deletes if not claimed |
| Claimed | Default 1h (max 24h via ttl_seconds) | User owns it, can extend TTL |
Flow:
create_preview(...) → preview starts with 20-min unclaimed TTLclaim_url → logs in with GitHub → TTL extends to full durationAlways tell the user: "Open this link to claim your preview before it expires: {claim_url}"
create_preview(mode="static", path="./public")
# 1. Create preview first to get the subdomain
create_preview(mode="static", name="my-blog")
# → returns subdomain "abc123", url "https://abc123.prevu.page"
# 2. Build with actual URL
hugo --minify -b "https://abc123.prevu.page/"
# 3. Zip and upload
cd public && zip -r /tmp/site.zip .
# upload /tmp/site.zip to the preview
create_preview(
mode="runtime", runtime="python:3.12", path="./backend",
start_command="python app.py", runtime_port=8080,
healthcheck_path="/health",
dependencies=[{type: "postgres", mode: "dsn", env_mapping: {DSN: "DATABASE_URL"}}]
)
# Build first!
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server .
mkdir -p /tmp/deploy && cp server /tmp/deploy/
create_preview(
mode="runtime", runtime="go", path="/tmp/deploy",
start_command="./server", runtime_port=8080,
healthcheck_path="/api/health"
)
Hugo, Jekyll, and similar SSGs bake the site URL into every HTML file at build time. If you build with a placeholder URL, all internal links and redirects will be broken. You must use the two-phase flow:
create_preview() first → get the subdomainhugo -b "https://xxx.prevu.page/")This applies to any SSG that uses absolute URLs in output. React SPAs with relative paths (e.g., Vite with base: './') don't have this issue.
If the project uses next-intl, next-auth, or any other middleware-based feature, output: 'export' will fail. You must use output: 'standalone' and deploy as a runtime preview (not static).
Standalone deploy recipe:
# 1. Add to next.config.ts/mjs:
# output: 'standalone'
# 2. Build
npm run build
# 3. Assemble deploy directory (all 3 parts required!)
mkdir -p /tmp/deploy
cp -r .next/standalone/* /tmp/deploy/
cp -r .next/static /tmp/deploy/.next/static
cp -r public /tmp/deploy/public # if exists
# 4. Deploy as runtime
create_preview(mode="runtime", runtime="node:22", path="/tmp/deploy",
start_command="node server.js", runtime_port=3000)
Why standalone? Regular .next is 300MB+; standalone is ~20-30MB (includes only needed node_modules).
python:*-slim images lack wget, curl, unzip. Prevu's init containers use Python's built-in urllib + zipfile instead. You don't need to handle this — just know that if you see init container errors about missing commands, it's expected behavior that's already handled.
Dependencies are installed to /app/.pylibs (not system site-packages). Prevu auto-injects PYTHONPATH=/app/.pylibs so your app finds them. No action needed — but if you manually set PYTHONPATH in env vars, make sure to include /app/.pylibs.
The preview runs on Linux amd64. If you build on macOS or Windows:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server .
Forgetting CGO_ENABLED=0 can cause dynamic linking issues even on Linux.
Large uploads (>100MB) are slow and may timeout. Always optimize:
standalone output (~20MB vs 300MB+). Include .next/standalone/* + .next/static + public/. See Next.js standalone recipe below.venv/ or __pycache__/ in the zipnode_modules/ — they are auto-installed by npm install on the server. Including them causes cross-platform binary incompatibility (e.g., @swc/helpers built on macOS won't work on Alpine Linux).*.prevu.page URLNEXT_PUBLIC_API_URL=https://xxx.prevu.page)*.prevu.page originThere is no service discovery between previews — you must pass URLs explicitly.
If you specify healthcheck_path, that endpoint must return HTTP 200. If your app has no health route, either:
GET /health → 200)healthcheck_path="/" if your root returns 200The pod won't become Ready until the healthcheck passes (Kubernetes readiness probe).
MODULE_NOT_FOUND / @swc/helpers error: You uploaded node_modules/ with the artifact. Remove node_modules/ from the zip — the server runs npm install and needs platform-native binaries (Alpine Linux). macOS/Windows node_modules won't work./ route — this is app-level, not PrevuAccess-Control-Allow-Origin headers for cross-subdomain requestsrequirements.txt is in the deploy directoryCGO_ENABLED=0 GOOS=linux GOARCH=amd64get_preview_status — init containers pulling images or installing deps can take 30-60s/: Normal if using next-intl — it redirects to /{locale}. Your app works fine.If MCP is not available, use curl:
# Create
curl -X POST https://api.prevu.cloud/api/v1/previews \
-H "Content-Type: application/json" \
-d '{"name":"my-app","mode":"static","ttl_seconds":3600,"source_type":"mcp"}'
# Upload (static/runtime)
curl -X POST https://api.prevu.cloud/api/v1/previews/{id}/upload -F "[email protected]"
# Status
curl https://api.prevu.cloud/api/v1/previews/{id}
# Destroy
curl -X POST https://api.prevu.cloud/api/v1/previews/{id}/destroy -H "Authorization: {token}"