Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion docs/roo-code-cloud/environments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ keywords:
- mise
- Initial Path
- Subdomain Routing
- Proxied Ports
- Direct Port Access
---

# Preview Environments
Expand Down Expand Up @@ -91,6 +93,7 @@ ports:
|-------|-------------|----------|
| `name` | Identifier for the port (used to generate environment variables) | Yes |
| `port` | The port number to expose | Yes |
| `proxied` | Whether traffic goes through the auth proxy (`true` by default). Set to `false` for direct port access (see [Direct Port Access](#direct-port-access-non-proxied)) | No |
| `initial_path` | Default path to append to the preview URL | No |
| `subdomain` | Subdomain to set on the `Host` header when forwarding requests to the app | No |

Expand All @@ -113,7 +116,10 @@ The name is converted to uppercase for the environment variable (e.g., `web` bec

### Limits

You can configure up to **4 named ports** per environment.
Port limits depend on whether ports are proxied (default) or non-proxied:

- **Proxied ports** (default): up to **10** per environment. These share a single internal port slot via multiplexing.
- **Non-proxied ports** (`proxied: false`): up to **1** per environment. Each non-proxied port consumes a dedicated port slot.

### Initial Path

Expand Down Expand Up @@ -189,6 +195,39 @@ Invalid examples:
- **Admin panels**: Serve an admin interface on `admin.localhost:3000` while the main app runs on the root domain
- **Subdomain APIs**: Frameworks like Rails can use `api.localhost:3000` to route API requests to a separate controller namespace

### Direct Port Access (Non-Proxied)

By default, all ports are proxied through an authentication layer that validates requests before forwarding them to your application. Setting `proxied: false` bypasses this proxy entirely and exposes the port directly on the sandbox domain.

:::tip[Try `unauthenticated: true` first]
If you just need to skip authentication (e.g., for a public-facing endpoint or webhook), use `unauthenticated: true` instead. It keeps the proxy in place while disabling the auth check, and doesn't count against the stricter non-proxied port limit. Only reach for `proxied: false` when the proxy itself is the problem.
:::
Comment thread
mrubens marked this conversation as resolved.

```yaml
ports:
- name: WEB
port: 3000
- name: METRICS
port: 9090
proxied: false
```

Use `proxied: false` only when the proxy layer itself is incompatible with your use case:

- **Direct socket access** for protocols that don't work through the HTTP proxy
- **Non-HTTP services** that need the raw port exposed without any intermediary

:::warning[Warning]
When `proxied` is `false`, the port is **completely exposed without authentication**, regardless of the `unauthenticated` setting. Non-proxied ports also count against a stricter limit (1 per environment vs. 10 for proxied ports). Only use this as a last resort when other options don't work.
:::

The `ROO_<NAME>_HOST` environment variable for a non-proxied port points to the direct sandbox domain instead of the preview proxy URL. Your application code doesn't need to change -- just use the injected variable as usual:

```typescript
// Works the same whether the port is proxied or not
const metricsUrl = process.env.ROO_METRICS_HOST || 'http://localhost:9090';
```

## 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:
Expand Down