# Process Execution

Daytona runs shell commands and long-running processes in sandboxes. Commands run inside the sandbox, so untrusted or generated commands execute in isolation from your application.

Process execution is available through the process module of a sandbox. Every command runs as a process: you can run a command and wait for its result, or start it in the background and control it through a handle. Sessions are shell processes that keep state, such as the working directory and environment variables, across the commands that run in them. To run Python, TypeScript, and JavaScript code, see [code execution](https://www.daytona.io/docs/en/v2/code-execution.md). For interactive terminals, see [pseudo terminal (PTY)](https://www.daytona.io/docs/en/v2/pty.md). For real-time logs from long-running processes, see [log streaming](https://www.daytona.io/docs/en/v2/log-streaming.md).

## Use cases

Process execution is used when the work in a sandbox is a shell command or a program instead of a code snippet.

- **Build and test**: package installs, compilers, and test runners run in the sandbox, and their exit code and output return to the application.
- **Agent tools**: an agent runs shell commands as tools and reads their results.
- **Servers and workers**: a web server or worker starts in the background, and the application checks its state, reads its logs, or stops it through its handle.
- **Multi-step shell work**: a session keeps the working directory and environment variables, so a sequence of commands runs as it does in one terminal.
- **Interactive programs**: input goes to a running command while its output streams back. Closing stdin tells the command that it has all its input.
- **Reconnects**: an application reconnects to a process it started earlier and continues to read its output.
- **Commands as another user**: a command runs as a specific user that exists in the image, or with a specific shell that the image installs.
- **Entrypoint checks**: the details and logs of the sandbox entrypoint show whether the image's main process started and what it printed.
- **Environment configuration**: environment variables set once apply to every process started after the update, including PTYs, sessions, and Git commands.

## Command execution

### Execute commands

Run a command and wait for it to exit. The result holds the exit code and output.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

response = sandbox.process.run("ls -la")
print(response.exit_code, response.stdout)
```

| **Parameter** | **Type** | **Description**                                                                                                                                                  |
| ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`timeout`** | **`float`** | Sets how many seconds **`run()`** waits. Without it, **`run()`** waits until the command exits. When the timeout elapses, **`reason`** is **`"timed_out"`**, **`exit_code`** is **`None`**, and the command keeps running. |

The response has the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| **`stdout`** | Holds the output of the stdout channel. |
| **`stderr`** | Holds the output of the stderr channel. |
| **`output`** | Holds the output of both channels in recorded order. |


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const response = await sandbox.process.run('ls -la')
console.log(response.exitCode, response.stdout)
```

| **Parameter** | **Type** | **Description**                                                                                                                                                         |
| ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`timeout`** | **`number`** | Sets how many milliseconds **`run()`** waits. Without it, **`run()`** waits until the command exits. When the timeout elapses, **`reason`** is **`'timed_out'`**, **`exitCode`** is **`undefined`**, and the command keeps running. |

The response has the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| **`stdout`** | Holds the output of the stdout channel. |
| **`stderr`** | Holds the output of the stderr channel. |
| **`output`** | Holds the output of both channels in recorded order. |


```bash
daytona sandbox exec {sandboxId} -- ls -la
```

- Arguments after **`--`** run without a shell.
- To use shell syntax, wrap the command in **`bash -c '...'`**.
- The CLI exits with the exit code of the command.
- When the global **`--timeout`** elapses first, the command keeps running.
- The default of **`--timeout`** is **`10m`**.


```bash
# waitMs parks the response until the process exits (max 600000). logs=text attaches the first log page.
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?waitMs=5000&logs=text' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"kind": "exec", "shellCommand": "ls -la"}'
```

- When **`waitMs`** elapses before the process exits, the response has **`state`** set to **`running`**.
- Wait for the process and read its logs with separate requests.
- The attached log page holds up to **`1000`** frames.
- Read longer output from **`/processes/{processId}/logs`**.


### Set the user and shell

Run a command as a specific user or with a specific shell. The user must exist in the image and the shell must be installed.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

response = sandbox.process.run("whoami", user="daytona", shell="bash")
print(response.output)
```


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const response = await sandbox.process.run('whoami', { user: 'daytona', shell: 'bash' })
console.log(response.output)
```


```bash
daytona sandbox exec {sandboxId} --user daytona -- bash -c whoami
```

- **`exec`** has no shell flag.
- Pass the shell as the program, as in the example.


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?waitMs=5000&logs=text' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"kind": "exec", "shellCommand": "whoami", "user": "daytona", "shell": "bash"}'
```


### Update the environment

Set or remove environment variables for every process started in the sandbox after the update, including PTYs, sessions, and the git commands that [git operations](https://www.daytona.io/docs/en/v2/git-operations.md) run. Running processes and sessions keep their environment. Removing a variable affects only variables set through an earlier update; variables defined by the image stay.


```python
# Processes started after this call inherit the change
sandbox.update_env({"LOG_LEVEL": "debug"}, unset=["HTTP_PROXY"])

response = sandbox.process.run("echo $LOG_LEVEL")
print(response.stdout)
```

| **Parameter** | **Type**             | **Description**                                         |
| ------------- | -------------------- | ------------------------------------------------------- |
| **`env`** | **`Mapping[str, str]`** | Adds or replaces variables. Each **`KEY=VALUE`** entry is at most 128 KiB, and **`env`** is at most 1 MiB in total. |
| **`unset`** | **`Sequence[str]`** | Removes variables. This parameter is keyword only. |

- macOS sandboxes also cap the environment that new processes inherit.
- The cap counts the existing variables plus the update.
- On macOS, the cap is 512 KiB, half of **`ARG_MAX`**.
- On macOS, each variable counts 8 bytes in addition to its **`KEY=VALUE`** text.


```typescript
// Processes started after this call inherit the change
await sandbox.updateEnv({ LOG_LEVEL: 'debug' }, { unset: ['HTTP_PROXY'] })

const response = await sandbox.process.run('echo $LOG_LEVEL')
console.log(response.stdout)
```

| **Parameter** | **Type**                 | **Description**               |
| ------------- | ------------------------ | ----------------------------- |
| **`env`** | **`Record<string, string>`** | Adds or replaces variables. Each **`KEY=VALUE`** entry is at most 128 KiB, and **`env`** is at most 1 MiB in total. |
| **`unset`** | **`string[]`** | Removes variables. |

- macOS sandboxes also cap the environment that new processes inherit.
- The cap counts the existing variables plus the update.
- On macOS, the cap is 512 KiB, half of **`ARG_MAX`**.
- On macOS, each variable counts 8 bytes in addition to its **`KEY=VALUE`** text.


```bash
# Processes started after this call inherit the change
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/env' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"env": {"LOG_LEVEL": "debug"}, "unset": ["HTTP_PROXY"]}'
```

| **Field** | **Type**   | **Description**               |
| --------- | ---------- | ----------------------------- |
| **`env`** | **`object`** | Adds or replaces variables. Each **`KEY=VALUE`** entry is at most 128 KiB, and **`env`** is at most 1 MiB in total. |
| **`unset`** | **`string[]`** | Removes variables. |

- macOS sandboxes also cap the environment that new processes inherit.
- The cap counts the existing variables plus the update.
- On macOS, the cap is 512 KiB, half of **`ARG_MAX`**.
- On macOS, each variable counts 8 bytes in addition to its **`KEY=VALUE`** text.


### Execute interactive commands

Run a command that reads input. Start the command with stdin open, write input to it, and close stdin when the command has all its input.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

handle = sandbox.process.start("pip uninstall requests", stdin="open")

# Send input to the command
handle.stdin("y\n")
handle.stdin_eof()

wait = handle.wait()
page = handle.logs()
for frame in page.items:
    print(f"[{frame.channel}]: {frame.data}")
print(wait.exit_code)
```

| **Parameter** | **Type** | **Description**       |
| ------------- | -------- | --------------------- |
| **`stdin`** | **`str`** | Sets the stdin of the command to **`"open"`** or **`"closed"`**. The default is **`"closed"`**. |


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const handle = await sandbox.process.start('pip uninstall requests', {
    stdin: 'open',
})

// Send input to the command
await handle.stdin('y\n')
await handle.stdinEof()

const wait = await handle.wait()
const page = await handle.logs()
for (const frame of page.items) {
    console.log(`[${frame.channel}]:`, frame.data)
}
console.log(wait.exitCode)
```

| **Parameter** | **Type** | **Description**       |
| ------------- | -------- | --------------------- |
| **`stdin`** | **`string`** | Sets the stdin of the command to **`'open'`** or **`'closed'`**. The default is **`'closed'`**. |


```bash
# Piped input is written to the command's stdin, followed by end of input
printf 'y\n' | daytona sandbox exec {sandboxId} -- pip uninstall requests
```


```bash
# Start a command with stdin open
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "kind": "exec",
  "shellCommand": "pip uninstall requests",
  "stdin": "open"
}'

# Send input to the process
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}/stdin' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "data": "y\n",
  "eof": true
}'

# Get process logs
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}/logs' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

| **Field** | **Type** | **Description**     |
| --------- | -------- | ------------------- |
| **`stdin`** | **`string`** | Sets the stdin of the command to **`open`** or **`closed`**. The default is **`closed`**. |

| **Field** | **Type**  | **Description**                       |
| --------- | --------- | ------------------------------------- |
| **`eof`** | **`boolean`** | Closes stdin after writing **`data`** when set to **`true`**. |


## Process control

### Start a background process

Start a process without waiting for it to exit. The SDK returns a handle that you can use to wait for, read logs from, and stop the process.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

handle = sandbox.process.start("python -m http.server 8000")
print(handle.id)
```

- The record and logs of a process started without **`name`** are removed 10 minutes after the process exits.
- To keep the record and logs, pass **`name`** or **`keep_logs="until_cleanup"`**.
- See [log retention](#log-retention).


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const handle = await sandbox.process.start('python -m http.server 8000')
console.log(handle.id)
```

- The record and logs of a process started without **`name`** are removed 10 minutes after the process exits.
- To keep the record and logs, pass **`name`** or **`keepLogs: 'until_cleanup'`**.
- See [log retention](#log-retention).


```bash
# Start a detached process (no waitMs). Returns 201 with the process record.
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"kind": "exec", "shellCommand": "python -m http.server 8000"}'
```

| **Field**  | **Type** | **Description**                                                                                                                                   |
| ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`keepLogs`** | **`string`** | Sets how long the record and logs are kept after the process exits. With **`on_exit_ttl`**, the record and logs are removed 10 minutes after the process exits. The default is **`on_exit_ttl`** without **`name`** and **`until_cleanup`** with **`name`**. |


### List processes

List the processes in a sandbox. Filter the list by state, kind, session, name, or PID.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

running = sandbox.process.list(state="running")
for info in running.items:
    print(info.id, info.name, info.state, info.pid)
```


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const running = await sandbox.process.list({ state: 'running' })
for (const info of running.items) {
    console.log(info.id, info.name, info.state, info.pid)
}
```


```bash
# Filters: --state running|terminal|all, --kind, --session-id, --name, --pid
daytona sandbox process list {sandboxId} --state running
```


```bash
# Filters: state=running|terminal|all, kind, sessionId, name, pid
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?state=running' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


### Process states

| **State**          | **Meaning**                                                                                           |
| ------------------ | ----------------------------------------------------------------------------------------------------- |
| **`starting`**     | The record exists; the process has not been spawned yet.                                              |
| **`provisioning`** | [Code execution](https://www.daytona.io/docs/en/v2/code-execution.md) only: the language kernel is being installed or started. Progress is reported on the **`setup`** log channel. |
| **`running`**      | The process has a PID and is executing.                                                               |
| **`terminal`**     | The process has finished. **`reason`**, **`exitCode`**, and **`signal`** describe how.                            |

| **Reason**      | **Meaning**                                                                                                   |
| --------------- | ------------------------------------------------------------------------------------------------------------- |
| **`exited`**    | Exited on its own; **`exitCode`** is set. A code execution that raised an exception exits with **`1`**.               |
| **`signaled`**  | Killed by a signal; **`signal`** is set. **`wait`** reports bare names (**`TERM`**, **`KILL`**), the API reports **`SIGTERM`**, **`SIGKILL`**. |
| **`timed_out`** | The **`timeoutMs`** set at start elapsed; the process was killed with **`SIGKILL`**. Also returned by **`wait`** itself when its own long-poll window elapses first, with no **`exitCode`**. |
| **`lost`**      | The process disappeared without the daemon observing its exit, for example after a sandbox stop and start, or when its session was deleted while it was still running. |
| **`failed`**    | The process could not be started or ran into a daemon-side error.                                             |

### Log retention

Choose how long a process record and its logs are kept after the process exits. The policy is set when the process starts. Once a record is removed, the process can no longer be waited on or read. See [log streaming](https://www.daytona.io/docs/en/v2/log-streaming.md#log-retention) for size caps and eviction.

| **Policy**          | **Behavior**                                                                                    |
| ------------------- | ----------------------------------------------------------------------------------------------- |
| **`until_cleanup`** | Kept until the process is deleted (**`DELETE /processes/{id}`**). Default for processes started with a **`name`**; the SDK **`pty`** namespace also uses it. |
| **`on_exit_ttl`**   | Kept for 10 minutes after exit, then removed. Default for unnamed processes; the SDK **`run`** methods always use it. |
| **`none`**          | Logs are discarded as soon as the process exits. The record is kept for 10 minutes, so **`wait`** still returns the exit result. |


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

# Keep the record and logs until delete() is called
handle = sandbox.process.start("npm test", keep_logs="until_cleanup")
handle.wait()

for frame in handle.logs().items:
    print(frame.data, end="")
handle.delete()
```


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

// Keep the record and logs until delete() is called
const handle = await sandbox.process.start('npm test', { keepLogs: 'until_cleanup' })
await handle.wait()

for (const frame of (await handle.logs()).items) {
    process.stdout.write(frame.data)
}
await handle.delete()
```


```bash
# Keep the record and logs until DELETE
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"kind": "exec", "shellCommand": "npm test", "keepLogs": "until_cleanup"}'

# Read logs; limit defaults to 1000 and is capped at 5000
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}/logs' \
  --header 'Authorization: Bearer YOUR_API_KEY'

# Remove the record and logs
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


### Wait for a process

Wait for a process to finish, or until a timeout elapses.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

handle = sandbox.process.start("npm run build")

# Wait up to 2 minutes; without timeout, wait() blocks until the process is terminal
result = handle.wait(timeout=120)
print(result.reason, result.exit_code)
```


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const handle = await sandbox.process.start('npm run build')

// Wait up to 2 minutes; without timeout, wait() blocks until the process is terminal
const result = await handle.wait({ timeout: 120_000 })
console.log(result.reason, result.exitCode)
```


```bash
# Wait up to 2 minutes; exits with the process exit code, or 8 if the timeout elapses first
daytona sandbox process wait {sandboxId} {processId} --timeout 2m
```


```bash
# Long-poll for the terminal state. Omitting timeoutMs waits 60 s; 0 waits until terminal.
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}/wait?timeoutMs=120000' \
  --header 'Authorization: Bearer YOUR_API_KEY'

# Response when the process exits:
# {"exitCode": 0, "reason": "exited"}
# Response when the wait window elapses first:
# {"reason": "timed_out"}
```


### Stop a process

Stop a running process by sending it a signal. The SDKs send the terminate signal.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

handle = sandbox.process.start("python -m http.server 8000")

# Sends SIGTERM
handle.kill()
```

| **Parameter**    | **Type**     | **Description**                                                                                |
| ---------------- | ------------ | ---------------------------------------------------------------------------------------------- |
| **`signal`** | **`SignalName`** | Sets the signal to send: **`TERM`**, **`KILL`**, **`INT`**, **`HUP`**, **`QUIT`**, **`USR1`**, or **`USR2`**. The default is **`"TERM"`**. |
| **`escalate_after`** | **`float`** | Sends **`escalate_to`** if the process is still running after this many seconds. The value is at most **`600`**. |
| **`escalate_to`** | **`SignalName`** | Sets the signal sent after **`escalate_after`**. The default is **`"KILL"`**. |

- A command in a session cannot be signaled on its own.
- To stop a command in a session, delete the session.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const handle = await sandbox.process.start('python -m http.server 8000')

// Sends SIGTERM
await handle.kill()
```

| **Parameter**   | **Type**        | **Description**                                                                                        |
| --------------- | --------------- | ------------------------------------------------------------------------------------------------------ |
| **`signal`** | **`ProcessSignal`** | Sets the signal to send: **`'TERM'`**, **`'KILL'`**, **`'INT'`**, **`'HUP'`**, **`'QUIT'`**, **`'USR1'`**, or **`'USR2'`**. |
| **`escalateAfter`** | **`number`** | Sends **`escalateTo`** if the process is still running after this many milliseconds. The value is at most **`600000`**. |
| **`escalateTo`** | **`ProcessSignal`** | Sets the signal sent after **`escalateAfter`**. The default is **`'KILL'`**. |

- A command in a session cannot be signaled on its own.
- To stop a command in a session, delete the session.


```bash
daytona sandbox process signal {sandboxId} {processId} TERM
```

| **Flag**           | **Type** | **Description**                                                                                                 |
| ------------------ | -------- | --------------------------------------------------------------------------------------------------------------- |
| **`--escalate-after`** | **`string`** | Sends **`--escalate-to`** if the process is still running after this delay. The value is a duration such as **`5s`**, at most **`10m`**. |
| **`--escalate-to`** | **`string`** | Sets the second signal, sent after **`--escalate-after`**. |

- Signal names have no **`SIG`** prefix: **`TERM`**, **`KILL`**, **`INT`**, **`HUP`**, **`QUIT`**, **`USR1`**, or **`USR2`**.


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}/signals' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"signal": "SIGTERM"}'
```

| **Field**         | **Type**  | **Description**                                                                        |
| ----------------- | --------- | -------------------------------------------------------------------------------------- |
| **`signal`** | **`string`** | Sets the signal to send, with the **`SIG`** prefix. An empty value sends **`SIGTERM`**. |
| **`escalateAfterMs`** | **`integer`** | Sends **`escalateTo`** if the process is still running after this delay. The value is at most **`600000`**. |
| **`escalateTo`** | **`string`** | Sets the signal sent after **`escalateAfterMs`**. The default is **`SIGKILL`**. |


### Reconnect to a process

Reconnect to a process from another worker or process. Processes are addressed by ID, so any client with the sandbox ID and the process ID can wait for, read logs from, and stop the process. To reconnect to an interpreter context, see [reconnect to a context](https://www.daytona.io/docs/en/v2/code-execution.md#reconnect-to-a-context).


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

# Worker A: start the process and persist the handle
handle = sandbox.process.start("npm run build")
saved = handle.to_json()  # {"sandboxId": "...", "processId": "...", "kind": "exec"}

# Worker B: reconnect and wait for the same process
handle = sandbox.process.connect(saved)
result = handle.wait()
```

- **`connect()`** takes the saved dict or a process ID and makes no request.
- A process that does not exist, or a dict saved from another sandbox, fails on the first call that uses the handle.
- A process name resolves only while the process runs.
- After the process exits, reconnect by ID.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

// Worker A: start the process and persist the handle
const handle = await sandbox.process.start('npm run build')
const saved = handle.toJSON() // { sandboxId: '...', processId: '...', kind: 'exec' }

// Worker B: reconnect and wait for the same process
const reconnected = sandbox.process.connect(saved)
const result = await reconnected.wait()
```

- **`connect()`** takes the saved object or a process ID and makes no request.
- A process that does not exist, or an object saved from another sandbox, fails on the first call that uses the handle.
- A process name resolves only while the process runs.
- After the process exits, reconnect by ID.


```bash
# --timeout defaults to 10m; raise it for long-running processes
daytona sandbox process wait {sandboxId} {processId} --timeout 1h

# Attach to the live output stream
daytona sandbox process attach {sandboxId} {processId}
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}/wait' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

| **Field**   | **Type**  | **Description**                                                                                                                                                 |
| ----------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`timeoutMs`** | **`integer`** | Sets how long the request waits for the process to exit. Without it, the request returns after 60 seconds with **`{"reason": "timed_out"}`** if the process is still running. Repeat the request, or pass **`0`** to wait until the process exits. |


### Entrypoint process

Get the details and logs of the sandbox entrypoint. It runs as a system process, so you can read it like any other process but not signal, write to, or delete it.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

# Entrypoint process details
info = sandbox.process.get("entrypoint")
print(f"Command: {info.argv or info.shell_command}")

# Entrypoint logs
handle = sandbox.process.connect("entrypoint")
for frame in handle.logs().items:
    print(f"[{frame.channel}]: {frame.data}")
```


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

// Entrypoint process details
const info = await sandbox.process.get('entrypoint')
console.log('Command:', info.argv ?? info.shellCommand)

// Entrypoint logs
const handle = sandbox.process.connect('entrypoint')
for (const frame of (await handle.logs()).items) {
    console.log(`[${frame.channel}]:`, frame.data)
}
```


```bash
# Get entrypoint process details
daytona sandbox process get {sandboxId} entrypoint

# Get entrypoint logs
daytona sandbox process logs {sandboxId} entrypoint

# Control-plane entrypoint logs
daytona sandbox logs {sandboxId} --entrypoint
```


```bash
# Get entrypoint process details
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/entrypoint' \
  --header 'Authorization: Bearer YOUR_API_KEY'

# Get entrypoint logs
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/entrypoint/logs' \
  --header 'Authorization: Bearer YOUR_API_KEY'

# Control-plane entrypoint logs
curl 'https://api.daytona.com/sandboxes/{sandboxId}/logs' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


## Sessions

A session is a long-lived shell that runs the commands you send to it. Commands in a session run one at a time, in order, and share the shell state, such as the working directory, exported variables, and functions. To run commands in parallel, use separate sessions or background processes.

A session is a process of kind `session`, and each command in it is an ordinary process with its own logs, exit code, timeout, and signals. Interrupting a command stops only that command, and the session keeps running. Sessions survive pause and resume, but not stop and start. [Interpreter contexts](https://www.daytona.io/docs/en/v2/code-execution.md#interpreter-contexts) run a language kernel instead of a shell.

### Create a session

Create a shell session. The working directory and environment variables of the session apply to all of its commands.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

session = sandbox.process.create_session(cwd="/tmp", env={"CI": "1"}, name="build-session")
print(session.id)
```

| **Parameter** | **Type** | **Description** |
| ------------- | -------- | --------------- |
| **`cwd`** | **`str`** | Sets the working directory of the shell. The default is the home directory of the default user. |
| **`env`** | **`Mapping[str, str]`** | Sets environment variables applied when the shell starts. |
| **`shell`** | **`str`** | Sets the path or name of the shell. On Linux and macOS, it must be a POSIX shell: **`bash`**, **`sh`**, **`dash`**, **`zsh`**, or **`ksh`**. |
| **`login`** | **`bool`** | Starts a login shell when set to **`True`**. |
| **`name`** | **`str`** | Sets a name that commands can use in place of the session ID while the session is running. |

- **`create_session()`** returns a **`Session`**. Use it as a context manager to delete the session when the block exits.
- **`session.to_json()`** and **`sandbox.process.connect_session()`** reconnect to a session without a request.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

const session = await sandbox.process.createSession({ cwd: '/tmp', env: { CI: '1' }, name: 'build-session' })
console.log(session.id)
```

| **Parameter** | **Type** | **Description** |
| ------------- | -------- | --------------- |
| **`cwd`** | **`string`** | Sets the working directory of the shell. The default is the home directory of the default user. |
| **`env`** | **`Record<string, string>`** | Sets environment variables applied when the shell starts. |
| **`shell`** | **`string`** | Sets the path or name of the shell. On Linux and macOS, it must be a POSIX shell: **`bash`**, **`sh`**, **`dash`**, **`zsh`**, or **`ksh`**. |
| **`login`** | **`boolean`** | Starts a login shell when set to **`true`**. |
| **`name`** | **`string`** | Sets a name that commands can use in place of the session ID while the session is running. |

- **`createSession()`** returns a **`Session`**. Use it with **`await using`** to delete the session when the scope exits.
- **`session.toJSON()`** and **`sandbox.process.connectSession()`** reconnect to a session without a request.


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"kind": "session", "cwd": "/tmp", "env": {"CI": "1"}, "name": "build-session"}'
```

| **Field** | **Type**  | **Description** |
| --------- | --------- | --------------- |
| **`kind`** | **`string`** | Set to **`session`**. |
| **`cwd`** | **`string`** | Sets the working directory of the shell. The default is the home directory of the default user. If that home directory does not exist, the default is the daemon working directory. |
| **`env`** | **`object`** | Sets environment variables applied when the shell starts. |
| **`shell`** | **`string`** | Sets the path or name of the shell. On Linux and macOS, it must be a POSIX shell: **`bash`**, **`sh`**, **`dash`**, **`zsh`**, or **`ksh`**. |
| **`login`** | **`boolean`** | Starts a login shell when set to **`true`**. The default is **`false`**. |
| **`user`** | **`string`** | Sets the user that the shell runs as. |
| **`name`** | **`string`** | Sets a name that commands can use in place of the session ID while the session is running. |

- The process ID in the response is the session ID.
- The request returns **`400`** **`PROCESS_START_FAILED`** when the shell does not run POSIX commands within 5 seconds of starting.


### Execute session commands

Run commands in a session. Shell state set by one command is available to the next. A command timeout starts when the command starts, after any commands queued ahead of it. When the timeout elapses, Daytona stops the command and the session keeps running.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

with sandbox.process.create_session() as session:
    # Shell state persists between commands
    session.run("cd /tmp && export STAGE=dev")
    result = session.run("pwd && echo $STAGE", execution_timeout=30)
    print(result.stdout)

    # Interrupt one command; the session keeps running
    command = session.start("sleep 600")
    command.kill(signal="INT")
```

| **Parameter** | **Type** | **Description** |
| ------------- | -------- | --------------- |
| **`timeout`** | **`float`** | Sets how many seconds **`run()`** waits for the command. |
| **`execution_timeout`** | **`float`** | Sets how many seconds the command can run before Daytona stops it. The session keeps running. |
| **`name`** | **`str`** | Sets a name for the command process. |


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create()

await using session = await sandbox.process.createSession()

// Shell state persists between commands
await session.run('cd /tmp && export STAGE=dev')
const result = await session.run('pwd && echo $STAGE', { executionTimeout: 30_000 })
console.log(result.stdout)

// Interrupt one command; the session keeps running
const command = await session.start('sleep 600')
await command.kill({ signal: 'INT' })
```

| **Parameter** | **Type** | **Description** |
| ------------- | -------- | --------------- |
| **`timeout`** | **`number`** | Sets how many milliseconds **`run()`** waits for the command. |
| **`executionTimeout`** | **`number`** | Sets how many milliseconds the command can run before Daytona stops it. The session keeps running. |
| **`name`** | **`string`** | Sets a name for the command process. |


```bash
# Shell state persists between commands
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?waitMs=60000&logs=text' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"kind": "exec", "shellCommand": "cd /tmp && export STAGE=dev", "sessionId": "build-session"}'

curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?waitMs=60000&logs=text' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{"kind": "exec", "shellCommand": "pwd && echo $STAGE", "sessionId": "build-session", "timeoutMs": 30000}'
```

- A command with **`sessionId`** must be **`kind: "exec"`** with **`shellCommand`**. **`sessionId`** takes the session ID or its name.
- A command with **`sessionId`** cannot set **`argv`**, **`cwd`**, **`env`**, **`shell`**, **`login`**, or **`user`**. The settings of the session apply.
- An unknown session returns **`SESSION_NOT_FOUND`**. A session that has ended returns **`SESSION_ENDED`**.
- A command that has not stopped 5 seconds after its timeout ends the session.


### Get session status

Get the status of a session, including the current working directory of its shell, and list the commands that ran in it.


```python
info = session.get()
print(info.state, info.cwd)

listed = sandbox.process.list(session_id=session.id)
for proc in listed.items:
    print(f"Process: {proc.id}, Command: {proc.shell_command}, Exit Code: {proc.exit_code}")
```


```typescript
const info = await session.get()
console.log(info.state, info.cwd)

const listed = await sandbox.process.list({ sessionId: session.id })
for (const proc of listed.items) {
  console.log(`Process: ${proc.id}, Command: ${proc.shellCommand}, Exit Code: ${proc.exitCode}`)
}
```


```bash
# Get the session process
daytona sandbox process get {sandboxId} {sessionId}

# List processes in a session
daytona sandbox process list {sandboxId} --session-id {sessionId}
```


```bash
# Get the session process
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{sessionId}' \
  --header 'Authorization: Bearer YOUR_API_KEY'

# List processes in a session
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?sessionId={sessionId}' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


### List sessions

List the shell sessions in a sandbox.


```python
sessions = sandbox.process.list(kind="session")
for proc in sessions.items:
    print(proc.id, proc.state)
```


```typescript
const sessions = await sandbox.process.list({ kind: 'session' })
for (const proc of sessions.items) {
  console.log(proc.id, proc.state)
}
```


```bash
daytona sandbox process list {sandboxId} --kind session
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?kind=session' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


### Get session command

Get a command that ran in a session by its process ID.


```python
command = sandbox.process.get("{processId}")
print(f"Command: {command.shell_command}, Exit Code: {command.exit_code}")
```


```typescript
const command = await sandbox.process.get('{processId}')
console.log(`Command: ${command.shellCommand}, Exit Code: ${command.exitCode}`)
```


```bash
daytona sandbox process get {sandboxId} {processId}
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


### Delete a session

Delete a session. This ends its shell, stops its running commands, and removes the records of its commands.


```python
session.delete()
```


```typescript
await session.delete()
```


```bash
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{sessionId}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_API_KEY'
```


## Resource management

Named processes keep their record and logs until you delete them. Delete a named process when you no longer need it; deleting stops the process if it is still running.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create()

handle = sandbox.process.start("sleep 3600", name="my-process")
try:
    pass  # Do work...
finally:
    handle.delete()  # kills the process if it is still running
```


```typescript
import { Daytona } from '@daytona/sdk';

const daytona = new Daytona();
const sandbox = await daytona.v2.create();

const handle = await sandbox.process.start("sleep 3600", { name: "my-process" });
try {
    // Do work...
} finally {
    await handle.delete(); // kills the process if it is still running
}
```


```bash
# Delete a process record and its logs; a running process is killed first
curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes/{processId}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_API_KEY'
```