> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ns.rocks/llms.txt
> Use this file to discover all available pages before exploring further.

# Run commands in background

> Start long-running commands and manage them by PID.

## Start a background process

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  server = machine.commands.run(
      "python3 -m http.server 8080 --bind 0.0.0.0",
      shell=True,
      cwd="/workspace",
      background=True,
  )
  print(server.pid)
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine exec mch_123 \
    --shell "python3 -m http.server 8080 --bind 0.0.0.0" --background
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -X POST "${NULLSPACE_API_URL}/v1/machines/mch_123/exec" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{"command": "python3 -m http.server 8080 --bind 0.0.0.0", "shell": true, "cwd": "/workspace", "background": true}'
  ```
</CodeGroup>

Without callbacks, background commands start through the REST exec endpoint and
return once the guest process has a PID. The command continues running inside
the machine until it exits, is killed, or the machine is paused or destroyed.

## Fetch logs and stop

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  logs = machine.commands.get_logs(server.pid)
  print(logs.stdout)

  machine.commands.kill(server.pid)
  result = server.wait()
  print(result.exit_code)
  ```

  ```typescript TypeScript SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  const logs = await machine.commands.getLogs(pid);
  console.log(logs.stdout);

  await machine.commands.kill(pid);
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine process logs mch_123 1234
  nullspace machine process kill mch_123 1234
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -X GET "${NULLSPACE_API_URL}/v1/machines/mch_123/processes/1234/logs" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}"

  curl -X POST "${NULLSPACE_API_URL}/v1/machines/mch_123/processes/1234/kill" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}"
  ```
</CodeGroup>

Background command objects also expose `kill()`:

```python theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
server.kill()
```

`BackgroundCommand` also supports:

* `server.get_logs()` to fetch `stdout`, `stderr`, interleaved `output`, and
  optional `exit_code`.
* `server.wait()` to block until exit. If there is no live WebSocket attached,
  it polls the process list and then fetches logs.
* `server.disconnect()` to close a live stream without killing the process.
* Iteration over `(stdout_chunk, stderr_chunk, None)` tuples; this polls logs
  for new output.

## Live background output

Pass callbacks with `background=True` when you want the process to keep running
and stream output immediately:

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  def on_stdout(chunk: str) -> None:
      print(chunk, end="")

  server = machine.commands.run(
      "python3 -u worker.py",
      shell=True,
      background=True,
      on_stdout=on_stdout,
  )
  server.disconnect()
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine process attach mch_123 1234
  ```
</CodeGroup>

This path uses the exec WebSocket to start the process and attach a reader to
the returned PID. Live background streaming is not a plain REST call: it runs
over `GET /v1/machines/{id}/ws?channel=exec`, so there is no `curl` equivalent.
Use the REST `GET /v1/machines/{id}/processes/{pid}/logs` endpoint to fetch
captured output after the process has started.

## Use with preview URLs

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  url = machine.get_url(8080)
  print(url)
  ```

  ```typescript TypeScript SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  const url = await machine.getUrl(8080);
  console.log(url);
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine url mch_123 8080
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -X POST "${NULLSPACE_API_URL}/v1/machines/mch_123/host" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{"port": 8080}'
  ```
</CodeGroup>

For web servers, bind to `0.0.0.0` inside the machine so preview URLs can reach
the process.

## Related

* [Preview URLs](../networking/preview-urls)
* [Processes](./processes)
* [WebSocket protocol](../reference/websocket-protocol)
