# Creating and Configuring Sandboxes — Configure sandbox compute, environment variables, and network access.


> *Render Sandboxes is in public beta.*
>
> APIs, defaults, and limits might change during the beta period.

You can create [sandboxes](sandboxes) from your own apps and agents with the Render CLI or SDK. This article describes how to create a sandbox and configure its compute plan, lifetime, network access, and more.

Before you begin, [install the CLI or SDK and set up your credentials](sandboxes#quickstart).

## Basic example

The following example creates a sandbox and waits until it's running:

**Tab: CLI**

```shell
render sandboxes create --timeout=300 --wait
```

Run [`render sandboxes create`](sandboxes-cli-reference#sandboxes-create) with flags for the sandbox's configuration options.

**Tab: TypeScript**

```typescript
import { Render } from '@renderinc/sdk'

const sandboxes = new Render().sandboxes

const sandbox = await sandboxes.create({
  timeoutSeconds: 300,
  wait: true
})

console.log(sandbox.id, sandbox.status)
```

Call [`sandboxes.create`](sandboxes-sdk-typescript#create) with an object containing the sandbox's configuration options.

**Tab: Python (sync)**

```python
from render import Render

sandboxes = Render().sandboxes

sandbox = sandboxes.create(
    timeout_seconds=300,
    wait=True,
)

print(sandbox.id, sandbox.status)
```

Call [`sandboxes.create`](sandboxes-sdk-python#create) with the sandbox's configuration options as keyword parameters.

**Tab: Python (async)**

```python
import asyncio
from render import RenderAsync

async def main():
    sandboxes = RenderAsync().sandboxes

    sandbox = await sandboxes.create(
        timeout_seconds=300,
        wait=True,
    )

    print(sandbox.id, sandbox.status)

asyncio.run(main())
```

Call [`sandboxes.create`](sandboxes-sdk-python#create) with the sandbox's configuration options as keyword parameters.

This example sets a five-minute [lifetime](#lifetime) and waits up to the default timeout of 60 seconds for the sandbox to become ready. After creation succeeds, you can [execute commands](sandboxes-executing) and [manage files](sandboxes-files) inside it.

*If you don't provide the `wait` option, the command returns without waiting.* Note that if the wait times out, the sandbox is not automatically terminated, and it might still be in the process of starting up.

## Configuring sandbox behavior

Provide configuration options when you create a sandbox.

### Compute plan

By default, a sandbox uses the *`1c-2g`* compute plan, which provides 1 CPU and 2 GB RAM. You can specify a different compute plan with the following syntax:

**Tab: CLI**

```shell
render sandboxes create \
  --wait \
  --plan=2c-4g # highlight-line
```

**Tab: TypeScript**

```typescript
const sandbox = await sandboxes.create({
  plan: '2c-4g', // highlight-line
  wait: true
})
```

**Tab: Python (sync)**

```python
sandbox = sandboxes.create(
    plan="2c-4g", # highlight-line
    wait=True,
)
```

**Tab: Python (async)**

```python
sandbox = await sandboxes.create(
    plan="2c-4g", # highlight-line
    wait=True,
)
```

For a list of available plans and their specs, see [Sandbox compute plans](sandboxes-limits#compute-plans).

When [restoring from a snapshot](sandboxes-snapshots), the new sandbox's plan defaults to matching the original sandbox's plan. Restoring a runtime snapshot _requires_ the same plan, whereas restoring a filesystem snapshot supports choosing a different plan.

### Lifetime

By default, a sandbox terminates after running continuously for 24 hours. This resets if the sandbox is [suspended](sandboxes-creating#suspension-on-idle) and later resumed.

You can customize this *lifetime* to any value between 60 seconds and 30 days (24 hours for *Hobby* workspaces).

Provide the lifetime in seconds:

**Tab: CLI**

```shell
render sandboxes create --timeout=3600 --wait
```

**Tab: TypeScript**

```typescript
const sandbox = await sandboxes.create({
  timeoutSeconds: 3600, // One hour
  wait: true
})
```

**Tab: Python (sync)**

```python
sandbox = sandboxes.create(
    timeout_seconds=3600, # One hour
    wait=True,
)
```

**Tab: Python (async)**

```python
sandbox = await sandboxes.create(
    timeout_seconds=3600, # One hour
    wait=True,
)
```

*When a sandbox terminates, its filesystem is permanently lost.* To preserve its state for future sandboxes, [capture a snapshot](sandboxes-snapshots) before it terminates.

### Suspension on idle

By default, Render suspends a sandbox after 15 minutes of being idle. A sandbox is considered idle if no [command execution](sandboxes-executing) or [file transfer](sandboxes-files) is in progress.

Executing a command or transferring files resumes a suspended sandbox automatically, _if_ the sandbox was suspended within the last 30 days.

You can change the idle timeout to any value between five minutes and 24 hours, or set it to `0` to disable automatic idle suspension:

**Tab: CLI**

```shell
render sandboxes create --idle-timeout=1800 --wait
```

To disable idle suspension, pass `--idle-timeout=0`.

**Tab: TypeScript**

```typescript
const sandbox = await sandboxes.create({
  idleTimeoutSeconds: 1800, // 30 minutes
  wait: true
})
```

To disable idle suspension, set `idleTimeoutSeconds: 0`.

**Tab: Python (sync)**

```python
sandbox = sandboxes.create(
    idle_timeout_seconds=1800, # 30 minutes
    wait=True,
)
```

To disable idle suspension, set `idle_timeout_seconds=0`.

**Tab: Python (async)**

```python
sandbox = await sandboxes.create(
    idle_timeout_seconds=1800, # 30 minutes
    wait=True,
)
```

To disable idle suspension, set `idle_timeout_seconds=0`.

Executing another command or transferring files resets the timeout. Checking a sandbox's status does not reset it.

Background processes do _not_ keep a sandbox active after the command that started them finishes. If you need background processes to continue without interruption, disable idle suspension.

### Environment variables

You can provide environment variables for commands that run inside a sandbox:

**Tab: CLI**

```shell
render sandboxes create \
  --env-var LOG_LEVEL=debug \
  --env-var API_BASE_URL=https://api.example.com \
  --wait
```

You can also load variables from an environment file:

```shell
render sandboxes create --env-file .env --env-var LOG_LEVEL=debug --wait
```

Repeat `--env-file` to load multiple files. Later files override earlier ones, and `--env-var` values override values loaded from files.

**Tab: TypeScript**

```typescript
const sandbox = await sandboxes.create({
  env: {
    LOG_LEVEL: 'debug',
    API_BASE_URL: 'https://api.example.com'
  },
  wait: true
})
```

**Tab: Python (sync)**

```python
sandbox = sandboxes.create(
    env={
        "LOG_LEVEL": "debug",
        "API_BASE_URL": "https://api.example.com",
    },
    wait=True,
)
```

**Tab: Python (async)**

```python
sandbox = await sandboxes.create(
    env={
        "LOG_LEVEL": "debug",
        "API_BASE_URL": "https://api.example.com",
    },
    wait=True,
)
```

See [General limits](sandboxes-limits#general-limits) for details on the maximum number and size of environment variables you can provide.

### Network access

By default, a sandbox can send outbound network traffic to any destination using any protocol. You can instead define an allowlist to restrict your sandbox's outbound traffic to specific HTTPS destinations:

**Tab: CLI**

```shell
render sandboxes create \
  --allow-egress-https api.github.com \
  --allow-egress-https '*.pypi.org' \
  --wait
```

To block all outbound traffic, pass `--network-policy=deny-all` instead of the `--allow-egress-https` flags.

**Tab: TypeScript**

```typescript
const sandbox = await sandboxes.create({
  networkPolicy: {
    type: 'allow-list',
    rules: [
      { domain: 'api.github.com' },
      { domain: '*.pypi.org' }
    ]
  },
  wait: true
})
```

To block all outbound traffic, provide `networkPolicy: { type: 'deny-all' }` instead.

**Tab: Python (sync)**

```python
from render.sandbox import EgressRule, NetworkPolicy

sandbox = sandboxes.create(
    network_policy=NetworkPolicy(
        type="allow-list",
        rules=[
            EgressRule(domain="api.github.com"),
            EgressRule(domain="*.pypi.org"),
        ],
    ),
    wait=True,
)
```

To block all outbound traffic, provide `network_policy="deny-all"` instead.

**Tab: Python (async)**

```python
from render.sandbox import EgressRule, NetworkPolicy

sandbox = await sandboxes.create(
    network_policy=NetworkPolicy(
        type="allow-list",
        rules=[
            EgressRule(domain="api.github.com"),
            EgressRule(domain="*.pypi.org"),
        ],
    ),
    wait=True,
)
```

To block all outbound traffic, provide `network_policy="deny-all"` instead.

- An allowlist supports up to 100 rules.
- Each rule specifies a lowercase hostname, without a scheme, port, or path.
- An exact hostname (such as `example.com`) does not include its subdomains.
  - Use a wildcard such as `*.example.com` to allow subdomains.

If you define an allowlist, Render blocks all traffic to disallowed destinations, _and_ all traffic using a protocol besides HTTPS (regardless of destination).

## Sandbox groups

Every sandbox belongs to a *sandbox group* in your workspace. The sandbox group determines the following:

- The sandbox's region
- The [snapshots](sandboxes-snapshots) available to restore when creating a sandbox in the group
- The sandbox's [concurrency limit](sandboxes-limits#concurrency-per-group)

When you create a sandbox without specifying a group, Render uses your workspace's default group. The first sandbox group created in a workspace is the default. If you create your first sandbox before creating any groups, Render automatically creates a default group for you.

You can create additional groups to organize separate workloads and set a concurrency limit for each.

### Finding groups

List your workspace's groups to find their IDs and identify the default group:

**Tab: CLI**

```shell
render sandbox-groups list
```

For the full group details, including the default-group indicator, use JSON output:

```shell
render sandbox-groups list --output json
```

**Tab: TypeScript**

```typescript
const entries = await sandboxes.listGroups()

for (const { sandboxGroup } of entries) {
  console.log(sandboxGroup.id, sandboxGroup.name, sandboxGroup.isDefault)
}
```

**Tab: Python (sync)**

```python
result = sandboxes.list_groups()

for group in result.groups:
    print(group.id, group.name, group.is_default)
```

**Tab: Python (async)**

```python
result = await sandboxes.list_groups()

for group in result.groups:
    print(group.id, group.name, group.is_default)
```

These examples return up to 20 groups. To retrieve additional groups, use the pagination options in the [TypeScript](sandboxes-sdk-typescript#listgroups) or [Python](sandboxes-sdk-python#list-groups) SDK reference.

Group IDs start with `sbg-`. A sandbox's group ID is also returned in its details as `sandboxGroupId` (TypeScript) or `sandbox_group_id` (Python).

### Creating a group

The following example creates a group named `agents`. Group names must be unique within your workspace:

**Tab: CLI**

```shell
render sandbox-groups create --name agents
```

**Tab: TypeScript**

```typescript
const group = await sandboxes.createGroup({ name: 'agents' })
console.log(group.id)
```

**Tab: Python (sync)**

```python
group = sandboxes.create_group("agents")
print(group.id)
```

**Tab: Python (async)**

```python
group = await sandboxes.create_group("agents")
print(group.id)
```

You can also create groups from the *+ New > Sandbox Group* menu in the [Render Dashboard](https://dashboard.render.com). In the Dashboard, you can optionally place a group in a project environment.

### Choosing a sandbox's group

Provide the group's ID when creating a sandbox. Replace `sbg-abc123` with your group ID:

**Tab: CLI**

```shell
render sandboxes create --group=sbg-abc123 --timeout=300 --wait
```

**Tab: TypeScript**

```typescript
const sandbox = await sandboxes.create({
  sandboxGroupId: 'sbg-abc123',
  timeoutSeconds: 300,
  wait: true
})
```

**Tab: Python (sync)**

```python
sandbox = sandboxes.create(
    sandbox_group_id="sbg-abc123",
    timeout_seconds=300,
    wait=True,
)
```

**Tab: Python (async)**

```python
sandbox = await sandboxes.create(
    sandbox_group_id="sbg-abc123",
    timeout_seconds=300,
    wait=True,
)
```

When creating a sandbox from a [snapshot](sandboxes-snapshots), the snapshot must belong to the selected group.

### Setting group concurrency

Use the SDK to set the maximum number of active sandboxes in a group. The following example sets a limit of 10:

**Tab: TypeScript**

```typescript
const group = await sandboxes.updateGroup('sbg-abc123', 10)
console.log(group.concurrencyLimit)
```

**Tab: Python (sync)**

```python
group = sandboxes.update_group("sbg-abc123", concurrency_limit=10)
print(group.concurrency_limit)
```

**Tab: Python (async)**

```python
group = await sandboxes.update_group("sbg-abc123", concurrency_limit=10)
print(group.concurrency_limit)
```

This limit must be at least 1 and is capped at 1,000 (even if you provide a higher value). For details, see [Concurrency per group](sandboxes-limits#concurrency-per-group).

## Finding existing sandboxes

You can list sandboxes in your workspace to find their IDs and statuses:

**Tab: CLI**

```shell
render sandboxes list --status running
```

To include terminated sandboxes, pass `--all` instead of `--status running`.

**Tab: TypeScript**

```typescript
const entries = await sandboxes.list({ status: ['running'] })

for (const { sandbox } of entries) {
  console.log(sandbox.id, sandbox.status)
}
```

To retrieve a single sandbox's details, call `await sandboxes.get('sbx-abc123')`.

**Tab: Python (sync)**

```python
page = sandboxes.list(status="running")

for sandbox in page.sandboxes:
    print(sandbox.id, sandbox.status)
```

To retrieve a single sandbox's details, call `sandboxes.from_id("sbx-abc123")`.

**Tab: Python (async)**

```python
page = await sandboxes.list(status="running")

for sandbox in page.sandboxes:
    print(sandbox.id, sandbox.status)
```

To retrieve a single sandbox's details, call `await sandboxes.from_id("sbx-abc123")`.

Without a status filter, sandbox lists exclude terminated sandboxes by default.

The SDKs return one "page" of results at a time. For pagination options, see the [TypeScript](sandboxes-sdk-typescript#list) and [Python](sandboxes-sdk-python#list) references.

## Terminating a sandbox

Terminate a sandbox when you no longer need it:

**Tab: CLI**

```shell
render sandboxes stop sbx-abc123 --confirm
```

Without `--confirm`, the command previews the operation and leaves the sandbox running.

**Tab: TypeScript**

```typescript
await sandboxes.terminate(sandbox.id)
```

**Tab: Python (sync)**

```python
sandboxes.terminate(sandbox.id)
```

**Tab: Python (async)**

```python
await sandboxes.terminate(sandbox.id)
```

*Termination is permanent.* You can no longer run commands in the sandbox or retrieve its files.

---

##### Appendix: Glossary definitions

###### compute plan

Specifies the CPU and RAM available to your service's *instances*.

Common compute plans for a new web service include:

- *`free`*: 0.1 CPU / 512 MB RAM
- *`0.5c-512mb`*: 0.5 CPU / 512 MB RAM
- *`1c-2g`*: 1 CPU / 2 GB RAM

Compute plans were previously known as *instance types*.

Related article: https://render.com/docs/compute-plans.md

###### region

Each Render service runs in one of the following regions: *Oregon*, *Ohio*, *Virginia*, *Frankfurt*, or *Singapore*.

Services in the same region can communicate over their *private network*.

Related article: https://render.com/docs/regions.md