# How to Run a Long Job in a Cloud Sandbox and Check on It Later

> Start the job with sbx exec --detach, which returns a process id at once, then close your terminal. Later, sbx exec --status shows whether it's still running, its exit code and the end of its output. Extend auto-stop past the job's length, because a running job keeps the sandbox awake but doesn't hold off auto-stop.

Source: https://sandboxer.com/guides/run-a-long-job-in-a-cloud-sandbox. Updated 2026-10-27.

**Short answer:** Start the job with sbx exec --detach, which returns a process id at once, then close your terminal. Later, sbx exec --status shows whether it's still running, its exit code and the end of its output. Extend auto-stop past the job's length, because a running job keeps the sandbox awake but doesn't hold off auto-stop.

## At a glance

| What you want | Command | What we saw |
| - | - | - |
| Start a job and walk away | `sbx exec $id --detach "<cmd>"` | Returns a `processId` and `commandId` at once |
| Is it done? What was the exit code? | `sbx exec $id --status $pid` | `running`, then `exited (exit 0)` with start and finish times |
| Watch it live | `sbx exec $id --attach $pid` | Prints the log from the start, then exits with the job's exit code |
| Read the output after it ends | `sbx history $id <cmd_id>` | The full stored stdout |
| Stop it | `sbx exec $id --kill $pid` | Exit code `143` (`SIGTERM`), listed as `killed` |
| Keep the deadline ahead of the job | `sbx extend $id --hours 2` | Auto-stop moves to now + 2 hours |

## Pick the right way to run it

| Job | Use | Why |
| - | - | - |
| Under 10 minutes, you wait for it | `sbx exec $id --timeout 600 "<cmd>"` | Synchronous commands stop at 600 seconds |
| Minutes to hours, you check later | `sbx exec --detach` | Survives your disconnect, and a stop and resume |
| Must run forever, survive a cold boot or a fork | A systemd service | Detached processes don't come back after a cold boot |
| A daemon whose work the API can't see | Any of the above plus `~/.sandboxer/keep-awake` | Stops idle pause from treating it as idle |

## 1. Start the job detached

We ran a 150-second job (30 steps, 5 seconds apart) on a `small` sandbox ($0.018/hour) on prod on 2026-10-27. Swap in your own build, test suite or training script.

```bash
id=$(sbx new --type small --json | jq -r 'select(.event == "ready") | .id')

sbx exec $id --detach 'for i in $(seq 1 30); do echo "step $i/30 $(date -u +%T)"; sleep 5; done; echo "job finished"'
```

```json
{"ok":true,"type":"command.started","commandId":"cmd_kzz60tfkzjkw","pid":523,"processId":523,
 "logPath":"/home/user/.sandboxer/processes/523.log","errLogPath":"/home/user/.sandboxer/processes/523.err.log",
 "startedAt":"2026-10-27T02:02:19.91067523Z","success":true, …}
```

Keep two values: `processId` (for `--status`, `--attach` and `--kill`) and `commandId` (for `sbx history`). In a script, capture the pid directly:

```bash
pid=$(sbx exec $id --detach "npm run build" | jq -r .processId)
```

The CLI returns at once. Close the terminal, shut the laptop, or let your CI job end. The job runs inside the VM, not on your machine.

## 2. Check on it later

```bash
sbx exec $id --status 523
```

```text
Process:  523
Status:   running
Command:  for i in $(seq 1 30); do echo "step $i/30 $(date -u +%T)"; sleep 5; done; echo "job finished"
Started:  2026-10-27T02:02:19.91067523Z
Log:      /home/user/.sandboxer/processes/523.log
--- stdout (tail) ---
step 1/30 02:02:19
```

We came back three minutes later:

```text
Process:   523
Status:    exited (exit 0)
Started:   2026-10-27T02:02:19.91067523Z
Finished:  2026-10-27T02:04:49.977383159Z
Log:       /home/user/.sandboxer/processes/523.log
--- stdout (tail) ---
step 1/30 02:02:19
…
step 30/30 02:04:44
job finished
```

In a script, wait for the job and read its exit code from the JSON:

```bash
while [ "$(sbx exec $id --status $pid --json | jq -r .running)" = true ]; do sleep 30; done
sbx exec $id --status $pid --json | jq '{status, exitCode, startedAt, finishedAt}'
```

```json
{
  "status": "exited",
  "exitCode": 3,
  "startedAt": "2026-10-27T02:13:39.178228158Z",
  "finishedAt": "2026-10-27T02:16:56.259617937Z"
}
```

That second job ended with `exit 3` on purpose. `sbx exec --attach` also passes the exit code through: it printed the log from step 1 to `job finished` and then exited with status `3`, so a CI step that attaches fails when the job fails.

## 3. Read the output after the job ends

Every command run through the API is recorded. `sbx history` lists them, newest first. It also works while the sandbox is stopped.

```bash
sbx history $id
```

```text
COMMAND-ID        STATUS  MODE      STARTED              COMMAND
cmd_11evxgkfczn9  exit 3  detached  2026-10-27 02:13:39  for i in $(seq 1 30); do echo "step $i/30 $(date -u +%T)"; sleep 5; d…
cmd_kzz60tfkzjkw  exit 0  detached  2026-10-27 02:02:19  for i in $(seq 1 30); do echo "step $i/30 $(date -u +%T)"; sleep 5; d…
…
```

```bash
sbx history $id cmd_11evxgkfczn9 | tail -3
```

```text
step 29/30 02:16:46
step 30/30 02:16:51
job finished
```

Add `--follow` to keep printing new output while the job runs, or `--stderr` for its error stream. Commands run through `exec` are kept for 90 days.

## 4. Keep the sandbox from idle-pausing

A sandbox pauses itself after `idleStopMinutes` without activity (by default 15 minutes on the free trial, 30 to 120 on paid plans). A detached command that is still running counts as activity. To check, we set the idle limit to 1 minute before the 150-second job:

```bash
sbx idle $id 1
```

```text
sb_ahaky0zrmj idle pause: after 1 idle minutes (wakes on use)
```

The job logged a step every 5 seconds with no gap, so the sandbox never paused. Once the job ended and nothing else touched the sandbox, it paused about 70 seconds after the last activity, and `sbx info` showed `"stop": {"reason": "idle"}`. The next `sbx exec` woke it and returned in 0.6 seconds (one measured run).

A sandbox also stays awake while the 1-minute load average is at least 0.5, or while `~/.sandboxer/keep-awake` exists. Use the file for work the API can't see, such as a server waiting on a queue:

```bash
sbx exec $id "mkdir -p ~/.sandboxer && touch ~/.sandboxer/keep-awake"
# later, to allow pausing again:
sbx exec $id "rm ~/.sandboxer/keep-awake"
```

With the file in place and the idle limit still at 1 minute, the sandbox stayed `ready` for the three minutes we waited. To turn idle pause off for one sandbox, run `sbx idle $id 0`.

## 5. Make auto-stop outlast the job

Auto-stop is a separate, hard deadline: one hour after start by default. **A running job doesn't hold it off.** We started the job again and cut the deadline to 60 seconds:

```bash
sbx extend $id --ttl 60
```

```text
sb_ahaky0zrmj auto-stop: 2026-10-27 02:14 (in 1m00s)
```

At 02:14:42 the sandbox stopped with `"reason": "auto_stop"`, mid-job. The stop saved memory, and the resume below restored it, so nothing was lost. The sandbox doesn't wake by itself after an auto-stop:

```bash
sbx exec $id --status 614
```

```text
error: The sandbox is stopped. Resume it first. (machine_not_running) [HTTP 409, req_5s6vTnD07PeYxIGUGHX6]
```

```bash
sbx resume $id
sbx exec $id --status 614
```

```text
Status:   running
…
step 13/30 02:14:39
step 14/30 02:15:31
```

The job picked up where it froze: step 13, a 52-second gap while the sandbox was stopped, then step 14. It finished normally after that. Memory comes back only when the resume lands on a machine with the same CPU type. If it doesn't, the sandbox boots from disk (`memoryRestored: false`), the job's status becomes `lost` and you rerun it; `sbx resume --require-memory` waits for a matching machine instead.

Resuming starts the countdown again from the sandbox's TTL. Ours was still 60 seconds, so it would have stopped again within two minutes. Extend right after you resume:

```bash
sbx extend $id --hours 1        # new deadline: now + 1 hour
sbx extend $id --no-auto-stop   # or no deadline at all
```

For a job you know is long, set the TTL when you create the sandbox (`sbx new --ttl 28800` for 8 hours). During the trial, TTLs above eight hours and `--no-auto-stop` are refused, so extend in steps or resume after an auto-stop.

## 6. Stop a job early

```bash
sbx exec $id --kill $pid
```

```text
Stopped process 528 and its children
```

`--status` then shows `exitCode` `143` and `signal` `SIGTERM`, and `sbx history` lists the command as `killed 143`. `sbx ps $id` lists live processes with their history id, so you can find a job you started without saving its pid.

## FAQ

### Does a detached job survive if I close my terminal?

Yes. The job runs inside the VM, and `sbx exec --detach` returns as soon as it starts. Check it later from any machine with your API key.

### Does a running job stop the sandbox from auto-stopping?

No. It stops idle pause, not auto-stop. Set `--ttl` on `sbx new` or run `sbx extend` so the deadline lands after the job ends.

### What happens to my job if the sandbox stops?

When the resume restores memory, the job continues from where it froze. In our test it went from step 13 to step 14 with no restart. If the resume cold-boots instead (for example on a different CPU type, shown as `memoryRestored: false`), the job's status becomes `lost`, and its log files stay on disk.

### Where does the job's output go?

To `~/.sandboxer/processes/<pid>.log` and `<pid>.err.log` in the sandbox. It's also kept in command history for 90 days, which you can read with `sbx history` even while the sandbox is stopped.

### How long can a single job run?

The 600-second limit applies only to synchronous commands; for anything longer, detach. The sandbox's auto-stop is what bounds the job, and it can be set up to 30 days or turned off.

## Related

- [Long-running tasks](/docs/long-running)
- [Pause and resume a cloud sandbox with its memory](/guides/pause-and-resume-a-sandbox-with-memory)
- [CLI reference](/docs/cli)
- [Limits and errors](/docs/limits)
