Preview Environments
Preview environments let you run multiple services in a cloud container with automatic domain routing. Each port you configure gets a unique public URL, and environment variables are injected so your applications can communicate with each other.
Overview
When you spawn an environment, Roo Code Cloud:
- Creates a cloud container with your configured ports exposed
- Generates unique HTTPS domains for each port
- Injects environment variables like
ROO_WEB_HOSTandROO_API_HOSTinto your container - Clones your repositories, starts services, and runs your commands
This allows you to run a complete stack—frontend, API, workers—in a single preview environment where all the pieces can talk to each other.
Configuration
Environments are configured in YAML format. Here's the complete schema:
name: My Full Stack App
description: Frontend and API running together
repositories:
- repository: myorg/frontend
commands:
- name: Install
run: npm install
- name: Start
run: npm run dev
detached: true
- repository: myorg/backend
commands:
- name: Install
run: npm install
- name: Start
run: npm run dev
detached: true
ports:
- name: WEB
port: 3000
- name: API
port: 3001
services:
- postgres16
- redis7
env:
NODE_ENV: development
Named Ports
The ports section defines which ports to expose and what to call them:
ports:
- name: WEB
port: 3000
- name: API
port: 3001
- name: ADMIN
port: 3002
For each named port, an environment variable is injected into your container:
| Port Config | Environment Variable | Example Value |
|---|---|---|
name: WEB, port: 3000 | ROO_WEB_HOST | https://abc123.vercel.run |
name: API, port: 3001 | ROO_API_HOST | https://def456.vercel.run |
name: ADMIN, port: 3002 | ROO_ADMIN_HOST | https://ghi789.vercel.run |
Naming Rules
Port names must:
- Start with a letter
- Contain only letters, numbers, and underscores
- Be 1-50 characters long
The name is converted to uppercase for the environment variable (e.g., web becomes ROO_WEB_HOST).
Limits
You can configure up to 4 named ports per environment.
Using Environment Variables in Your Code
Use the ROO_<NAME>_HOST variables instead of hardcoded URLs so your services can find each other in both preview and local environments:
// Backend: configure CORS with the injected frontend URL
app.use(cors({
origin: process.env.ROO_WEB_HOST || 'http://localhost:3000'
}));
// Frontend (Vite): pass the API URL at build time
// vite.config.ts
export default defineConfig({
define: {
'import.meta.env.API_URL': JSON.stringify(process.env.ROO_API_HOST || 'http://localhost:3001')
}
})
For static site frameworks (Vite, Next.js, CRA), the API URL needs to be set at build time via command-level env:
commands:
- name: Build
run: npm run build
env:
VITE_API_URL: ${ROO_API_HOST}
Repositories
List the repositories to clone into your environment:
repositories:
- repository: myorg/frontend
commands:
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Start dev server
run: npm run dev
detached: true
logfile: /tmp/frontend.log
- repository: myorg/backend
commands:
- name: Install dependencies
run: npm install
- name: Run migrations
run: npm run db:migrate
- name: Start server
run: npm run start
detached: true
logfile: /tmp/backend.log
Repository Format
Use the owner/repo format (e.g., myorg/my-app).
Commands
Each repository can have its own commands that run in order. Commands support:
| Field | Description | Default |
|---|---|---|
name | Display name for the command | Required |
run | The shell command to execute | Required |
working_dir | Relative directory to run the command in | Repository root |
cwd | Absolute path to run the command in | Repository root |
env | Command-specific environment variables | None |
timeout | Maximum seconds to wait | 60 |
continue_on_error | Keep going if command fails | false |
detached | Run in the background (see below) | false |
logfile | File path to write stdout/stderr when detached is true | None |
Background Processes
To start a long-running process like a dev server, use detached: true. This runs the command in the background so subsequent commands can execute immediately:
commands:
- name: Install
run: npm install
- name: Start dev server
run: npm run dev
detached: true
logfile: /tmp/dev-server.log
When detached is true, the command runs via nohup and the worker moves on after confirming the process started. If logfile is set, stdout and stderr are written to that path — useful for debugging startup issues.
Automatic Tool Installation
If a repository contains a .tool-versions file (used by mise / asdf), Roo Code Cloud automatically runs mise install after cloning. This installs the correct versions of tools like Node.js, Python, Ruby, Go, or any other runtime your project requires — before any of your setup commands execute.
# .tool-versions
nodejs 20.11.0
python 3.12.1
Services
Add managed database and cache services:
services:
- redis7
- postgres16
Available Services
| Service | Default Port | Connection Variables |
|---|---|---|
redis6 | 6379 | REDIS_URL |
redis7 | 6379 | REDIS_URL |
postgres15 | 5432 | DATABASE_URL, POSTGRES_* |
postgres16 | 5432 | DATABASE_URL, POSTGRES_* |
postgres17 | 5432 | DATABASE_URL, POSTGRES_* |
mysql8 | 3306 | DATABASE_URL, MYSQL_* |
mariadb10 | 3306 | DATABASE_URL, MARIADB_* |
clickhouse | 9000 | CLICKHOUSE_URL |
Custom Ports
If you need a service on a non-default port:
services:
- name: postgres16
port: 5433
Environment Variables
Define environment variables available to all commands:
env:
NODE_ENV: development
LOG_LEVEL: debug
FEATURE_FLAGS: "new-ui,beta-api"
These are merged with:
- Service connection variables (e.g.,
DATABASE_URL) - Named port variables (e.g.,
ROO_WEB_HOST) - Command-specific variables (highest priority)
Complete Example
Here's a full-stack application with a React frontend, Hono API, and background worker:
name: E-Commerce Platform
description: Full stack with frontend, API, and worker
repositories:
- repository: acme/storefront
commands:
- name: Install
run: npm install
- name: Build
run: npm run build
env:
VITE_API_URL: ${ROO_API_HOST}
- name: Serve
run: npx serve -s dist -l 3000
detached: true
logfile: /tmp/storefront.log
- repository: acme/api
commands:
- name: Install
run: npm install
- name: Migrate
run: npm run db:push
- name: Start
run: npm run start
detached: true
logfile: /tmp/api.log
env:
ALLOWED_ORIGINS: ${ROO_WEB_HOST}
- repository: acme/worker
commands:
- name: Install
run: npm install
- name: Start
run: npm run start
detached: true
logfile: /tmp/worker.log
ports:
- name: WEB
port: 3000
- name: API
port: 3001
- name: WORKER
port: 3002
services:
- postgres16
- redis7
env:
NODE_ENV: production
LOG_LEVEL: info
After the environment starts, you'll get unique URLs for each port. Visit the WEB URL to access your running application.
Common Issues
CORS Errors
In a preview environment, your frontend and backend run on different domains (e.g., https://abc123.vercel.run and https://def456.vercel.run). Browsers block cross-origin requests by default, so your backend needs to explicitly allow the frontend's domain.
Use the ROO_WEB_HOST variable to configure your backend's CORS policy:
Express:
import cors from 'cors';
app.use(cors({
origin: process.env.ROO_WEB_HOST || 'http://localhost:3000'
}));
Hono:
import { cors } from 'hono/cors';
app.use(cors({
origin: process.env.ROO_WEB_HOST || 'http://localhost:3000'
}));
Fastify:
app.register(import('@fastify/cors'), {
origin: process.env.ROO_WEB_HOST || 'http://localhost:3000'
});
Then in your environment config, make sure both ports are defined so the variables get injected:
ports:
- name: WEB
port: 3000
- name: API
port: 3001
Managing Frontend API URLs with .env Files
Frontends typically need the API URL at build time. If your project already uses .env files (via dotenv, dotenvx, or framework built-ins like Vite's .env.local), you can write the injected ROO_API_HOST into a .env file as a setup command — no code changes needed:
repositories:
- repository: myorg/frontend
commands:
- name: Configure API URL
run: echo "VITE_API_URL=${ROO_API_HOST}" >> .env.local
- name: Install
run: npm install
- name: Start
run: npm run dev
detached: true
This works with any framework that reads .env files:
| Framework | File | Variable prefix |
|---|---|---|
| Vite | .env.local | VITE_ |
| Next.js | .env.local | NEXT_PUBLIC_ |
| Create React App | .env.local | REACT_APP_ |
This approach keeps your environment config simple and avoids modifying application code.
Tips
- Use
ROO_*_HOSTvariables, not hardcoded URLs. Always fall back to localhost for local dev:process.env.ROO_API_HOST || 'http://localhost:3001'. - Use consistent uppercase port names.
WEB,API,ADMIN— notfrontend,BACKEND_API,Admin_Panel.