Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions box/overall/how-it-works.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ A box retains its full state between runs (files, installed packages, git histor

When you create a box, Upstash provisions a new isolated container with its own filesystem, shell, and network stack. You can start from a fresh box or restore from a snapshot. Once provisioning finishes, the box is ready to receive commands.

If the box has an [init command](/box/overall/quickstart#init-command), the box runs it once after the container starts, on create, on resume, and after a snapshot restore.

### 2. Running

The box automatically enters Running state after creation. Your agent can run bash commands, read and write files, interact with git, and make outbound network requests. `stdout` and `stderr` stream back in real-time.
Expand Down Expand Up @@ -248,6 +250,8 @@ box.resume()

Paused boxes do not accrue active CPU charges. Pause/resume is not available when `keepAlive` is enabled.

Resuming reruns the box's [init command](/box/overall/quickstart#init-command), so a server started that way comes back with the box.

### Snapshot and restore

Snapshots are the best way to turn a prepared environment into a reusable starting point, especially installing dependencies.
Expand Down
35 changes: 3 additions & 32 deletions box/overall/keep-alive.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ If you do not need the box to remain continuously available, keep `keepAlive` di

## Init command

Boxes with `keepAlive` enabled can run a startup command whenever the box starts.
A keep-alive box can run an init command, a startup script the box runs once each time its container starts.

<CodeGroup>
```typescript box.ts
Expand All @@ -87,36 +87,7 @@ box = Box.create(
```
</CodeGroup>

This is useful for:

- starting a web server
- launching a background process
- preparing a long-running agent environment
- restoring a development workflow automatically after the box starts

You can also manage the init command after creation:

<CodeGroup>
```typescript box.ts
await box.setInitCommand("npm run dev")

const command = await box.getInitCommand()
await box.deleteInitCommand()

console.log(box.keepAlive) // true
```

```python box.py
box.set_init_command("npm run dev")

command = box.get_init_command()
box.delete_init_command()

print(box.keep_alive) # True
```
</CodeGroup>

Init command management is only available when `keepAlive` is enabled.
Init commands are not a keep-alive feature. Every box can have one, and get, set, and delete work on any box, including a paused one. See [Create a Box](/box/overall/quickstart#init-command).

---

Expand All @@ -126,7 +97,7 @@ In the Upstash Console you can:

- enable **Keep alive** while creating a box
- choose the box **Size**
- manage the **Init Command** later from the box settings page
- manage the **Init Command** later from the box settings page (available on every box, not only keep-alive ones)

---

Expand Down
47 changes: 42 additions & 5 deletions box/overall/preview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,10 @@ const { publicURLs } = await box.listPublicURLs()

console.log(publicURLs)
// [
// { url: "https://{BOX_ID}-3000.preview.box.upstash.com", port: 3000 },
// { url: "https://{BOX_ID}-8080.preview.box.upstash.com", port: 8080 },
// { id: "{BOX_ID}-3000", port: 3000, url: "https://{BOX_ID}-3000.preview.box.upstash.com",
// created_at: 1789990404, basic_auth: false, bearer_token: false },
// { id: "{BOX_ID}-8080", port: 8080, url: "https://{BOX_ID}-8080.preview.box.upstash.com",
// created_at: 1789990512, basic_auth: false, bearer_token: true },
// ]
```

Expand Down Expand Up @@ -272,9 +274,44 @@ public_url2 = box.get_public_url(3000, bearer_token=True)

### Public URL Lifecycle

Public URLs expire automatically when:
- The box is paused
- The box is deleted
A public URL outlives a pause. While the box is paused, a request to the URL resumes it. See [Waking a Paused Box](#waking-a-paused-box).

Public URLs are removed when the box is deleted.

### Waking a Paused Box

An incoming HTTP request to a public URL resumes its box if the box is paused. The request is held while the box comes back and until the app's port is listening, so the caller gets the app's real response instead of an error. If the public URL requires authentication, the credentials are checked before the box is woken.

After container resume and configuration finish, the proxy waits up to 30 additional seconds for the app to become ready. The total HTTP request can take longer than 30 seconds. If the app is still not ready when that wait expires, the request returns `503` with a `Retry-After: 5` header: a browser gets a page that refreshes itself, and any other client gets a plain `503` to retry. The box is already resumed, and the app can continue starting in the background. Move slow dependency installation, such as a cold `npm install`, into the image or a snapshot so the [init command](/box/overall/quickstart#init-command) only needs to start the app.

Set a client timeout that allows for both container resume and the app readiness wait. Both examples below use 90 seconds; increase it if your environment needs more time. Some clients, including httpx, default to a shorter timeout than a cold start takes.

<CodeGroup>
```typescript box.ts
const publicUrl = await box.getPublicURL(3000)

await box.pause()

// This request resumes the box and returns the app's response
const response = await fetch(publicUrl.url, { signal: AbortSignal.timeout(90_000) })
console.log(response.status) // 200, or 503 if the app is still starting
```

```python box.py
import httpx

public_url = box.get_public_url(3000)

box.pause()

# This request resumes the box and returns the app's response.
# httpx defaults to a 5 second timeout, which a cold start can outlast.
response = httpx.get(public_url.url, timeout=90.0)
print(response.status_code) # 200, or 503 if the app is still starting
```
</CodeGroup>

For the app to answer on its own after a resume, the box needs to start its server again. An [init command](/box/overall/quickstart#init-command) does that: the box runs it once every time the container starts, including after a resume.

### Auto-Resume

Expand Down
26 changes: 25 additions & 1 deletion box/overall/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,33 @@ box = Box.create(runtime="node")
Your box is ready to use! You can already use it as a standalone, secure, isolated sandbox with full shell access, git, and filesystem operations.

<Tip>
You can also create a keep-alive box by setting `keepAlive: true`. Keep-alive boxes stay on between sessions and can run an `initCommand` at startup. See [Keep Alive](/box/overall/keep-alive).
You can also create a keep-alive box by setting `keepAlive: true`. Keep-alive boxes stay on between sessions instead of auto-pausing. See [Keep Alive](/box/overall/keep-alive).
</Tip>

#### Init command

Pass `initCommand` to run a startup script each time the box starts. It runs once per container start, on create, on resume, and after a snapshot restore, so it is the place to start a server that should come back with the box.

<CodeGroup>
```typescript box.ts
const box = await Box.create({
runtime: "node",
initCommand: `node -e 'require("http").createServer((_, res) => res.end("Hello from Box!")).listen(3000)'`,
})
```

```python box.py
box = Box.create(
runtime="node",
init_command="""node -e 'require("http").createServer((_, res) => res.end("Hello from Box!")).listen(3000)'""",
)
```
</CodeGroup>

This example uses Node.js's built-in HTTP server, so it works in a fresh box without installing packages. The command runs from `/workspace/home`. For an existing app, prepare its project files and dependencies before setting its startup command.

You can read, change, or remove the command later with `getInitCommand`, `setInitCommand`, and `deleteInitCommand` (`get_init_command`, `set_init_command`, `delete_init_command` in Python), on any box including a paused one. A box restored from a snapshot does not inherit the snapshot's init command, so pass it again on the restore.

---

### 5. Configure an Agent (optional)
Expand Down
112 changes: 74 additions & 38 deletions llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9493,6 +9493,8 @@ A box retains its full state between runs (files, installed packages, git histor

When you create a box, Upstash provisions a new isolated container with its own filesystem, shell, and network stack. You can start from a fresh box or restore from a snapshot. Once provisioning finishes, the box is ready to receive commands.

If the box has an [init command](/docs/box/overall/quickstart#init-command), the box runs it once after the container starts, on create, on resume, and after a snapshot restore.

### 2. Running

The box automatically enters Running state after creation. Your agent can run bash commands, read and write files, interact with git, and make outbound network requests. `stdout` and `stderr` stream back in real-time.
Expand Down Expand Up @@ -9653,6 +9655,8 @@ box.resume()

Paused boxes do not accrue active CPU charges. Pause/resume is not available when `keepAlive` is enabled.

Resuming reruns the box's [init command](/docs/box/overall/quickstart#init-command), so a server started that way comes back with the box.

### Snapshot and restore

Snapshots are the best way to turn a prepared environment into a reusable starting point, especially installing dependencies.
Expand Down Expand Up @@ -9805,7 +9809,7 @@ If you do not need the box to remain continuously available, keep `keepAlive` di

## Init command

Boxes with `keepAlive` enabled can run a startup command whenever the box starts.
A keep-alive box can run an init command, a startup script the box runs once each time its container starts.

<CodeGroup>
```typescript box.ts
Expand All @@ -9825,36 +9829,7 @@ box = Box.create(
```
</CodeGroup>

This is useful for:

* starting a web server
* launching a background process
* preparing a long-running agent environment
* restoring a development workflow automatically after the box starts

You can also manage the init command after creation:

<CodeGroup>
```typescript box.ts
await box.setInitCommand("npm run dev")

const command = await box.getInitCommand()
await box.deleteInitCommand()

console.log(box.keepAlive) // true
```

```python box.py
box.set_init_command("npm run dev")

command = box.get_init_command()
box.delete_init_command()

print(box.keep_alive) # True
```
</CodeGroup>

Init command management is only available when `keepAlive` is enabled.
Init commands are not a keep-alive feature. Every box can have one, and get, set, and delete work on any box, including a paused one. See [Create a Box](/docs/box/overall/quickstart#init-command).

***

Expand All @@ -9864,7 +9839,7 @@ In the Upstash Console you can:

* enable **Keep alive** while creating a box
* choose the box **Size**
* manage the **Init Command** later from the box settings page
* manage the **Init Command** later from the box settings page (available on every box, not only keep-alive ones)

***

Expand Down Expand Up @@ -10708,8 +10683,10 @@ const { publicURLs } = await box.listPublicURLs()

console.log(publicURLs)
// [
// { url: "https://{BOX_ID}-3000.preview.box.upstash.com", port: 3000 },
// { url: "https://{BOX_ID}-8080.preview.box.upstash.com", port: 8080 },
// { id: "{BOX_ID}-3000", port: 3000, url: "https://{BOX_ID}-3000.preview.box.upstash.com",
// created_at: 1789990404, basic_auth: false, bearer_token: false },
// { id: "{BOX_ID}-8080", port: 8080, url: "https://{BOX_ID}-8080.preview.box.upstash.com",
// created_at: 1789990512, basic_auth: false, bearer_token: true },
// ]
```

Expand Down Expand Up @@ -10772,9 +10749,44 @@ public_url2 = box.get_public_url(3000, bearer_token=True)

### Public URL Lifecycle

Public URLs expire automatically when:
* The box is paused
* The box is deleted
A public URL outlives a pause. While the box is paused, a request to the URL resumes it. See [Waking a Paused Box](#waking-a-paused-box).

Public URLs are removed when the box is deleted.

### Waking a Paused Box

An incoming HTTP request to a public URL resumes its box if the box is paused. The request is held while the box comes back and until the app's port is listening, so the caller gets the app's real response instead of an error. If the public URL requires authentication, the credentials are checked before the box is woken.

After container resume and configuration finish, the proxy waits up to 30 additional seconds for the app to become ready. The total HTTP request can take longer than 30 seconds. If the app is still not ready when that wait expires, the request returns `503` with a `Retry-After: 5` header: a browser gets a page that refreshes itself, and any other client gets a plain `503` to retry. The box is already resumed, and the app can continue starting in the background. Move slow dependency installation, such as a cold `npm install`, into the image or a snapshot so the [init command](/docs/box/overall/quickstart#init-command) only needs to start the app.

Set a client timeout that allows for both container resume and the app readiness wait. Both examples below use 90 seconds; increase it if your environment needs more time. Some clients, including httpx, default to a shorter timeout than a cold start takes.

<CodeGroup>
```typescript box.ts
const publicUrl = await box.getPublicURL(3000)

await box.pause()

// This request resumes the box and returns the app's response
const response = await fetch(publicUrl.url, { signal: AbortSignal.timeout(90_000) })
console.log(response.status) // 200, or 503 if the app is still starting
```

```python box.py
import httpx

public_url = box.get_public_url(3000)

box.pause()

# This request resumes the box and returns the app's response.
# httpx defaults to a 5 second timeout, which a cold start can outlast.
response = httpx.get(public_url.url, timeout=90.0)
print(response.status_code) # 200, or 503 if the app is still starting
```
</CodeGroup>

For the app to answer on its own after a resume, the box needs to start its server again. An [init command](/docs/box/overall/quickstart#init-command) does that: the box runs it once every time the container starts, including after a resume.

### Auto-Resume

Expand Down Expand Up @@ -11153,9 +11165,33 @@ box = Box.create(runtime="node")
Your box is ready to use! You can already use it as a standalone, secure, isolated sandbox with full shell access, git, and filesystem operations.

<Tip>
You can also create a keep-alive box by setting `keepAlive: true`. Keep-alive boxes stay on between sessions and can run an `initCommand` at startup. See [Keep Alive](/docs/box/overall/keep-alive).
You can also create a keep-alive box by setting `keepAlive: true`. Keep-alive boxes stay on between sessions instead of auto-pausing. See [Keep Alive](/docs/box/overall/keep-alive).
</Tip>

#### Init command

Pass `initCommand` to run a startup script each time the box starts. It runs once per container start, on create, on resume, and after a snapshot restore, so it is the place to start a server that should come back with the box.

<CodeGroup>
```typescript box.ts
const box = await Box.create({
runtime: "node",
initCommand: `node -e 'require("http").createServer((_, res) => res.end("Hello from Box!")).listen(3000)'`,
})
```

```python box.py
box = Box.create(
runtime="node",
init_command="""node -e 'require("http").createServer((_, res) => res.end("Hello from Box!")).listen(3000)'""",
)
```
</CodeGroup>

This example uses Node.js's built-in HTTP server, so it works in a fresh box without installing packages. The command runs from `/workspace/home`. For an existing app, prepare its project files and dependencies before setting its startup command.

You can read, change, or remove the command later with `getInitCommand`, `setInitCommand`, and `deleteInitCommand` (`get_init_command`, `set_init_command`, `delete_init_command` in Python), on any box including a paused one. A box restored from a snapshot does not inherit the snapshot's init command, so pass it again on the restore.

***

### 5. Configure an Agent (optional)
Expand Down
Loading