# Tutorial: Running Code in a Sandbox
source: https://docs.chalk.ai/docs/sandboxes/tutorial

## Start a sandbox, run code in it, save files to a volume, read them from a second sandbox, and clean up.

A sandbox is an isolated container that you start from
Python, run commands in, and terminate when you are done. In this tutorial you
will:

- Start a sandbox and run a command in it
- Run code that needs a package
- Save files to a volume, so that they outlive the
sandbox that wrote them
- Read those files from a second sandbox
- Delete everything you created

### Before you start

You need:

- A Chalk account and an environment to work in.
- A role that can create sandboxes and run commands in them: the
deploy.create and deploy.redeploy permissions. If you created your Chalk
team yourself, you already have them. Otherwise, ask an admin for a role that
includes them. See Permissions.
- Sandboxes turned on in that environment. They are on by default in
environments that Chalk hosts, including Chalk Serverless. In a self-hosted
environment, see
One-time setup for admins.

You do not need a Chalk project or chalk init, and you do not deploy anything
with chalk apply. Every sandbox and volume in this tutorial is created by the
tutorial and deleted in the last step.

Set up the Python environment that will run the tutorial:

- If you followed the Setup Guide, chalk init already
installed chalkcompute in your project. Start Python from your project
folder with uv run python, or open a Jupyter notebook that uses the
project's .venv.
- Otherwise, install the Chalk CLI,
run chalk login, and install chalkcompute with pip install chalkcompute.
Start Python, or a Jupyter notebook, from the folder where you ran
chalk login, so that chalkcompute finds your login. See
Logins and environments.

Run each step's code in one Python session, in order. Later steps use the
sandboxes and the volume that earlier steps create.

### Step 1: Start a sandbox and run a command

```
import chalkcompute

sandbox = chalkcompute.Sandbox(
    image=chalkcompute.Image.debian_slim("3.12"),
    name="tutorial-first",
).run()

result = sandbox.exec("python3", "--version")
print(result.stdout_text)
```

```
Python 3.12.15
```

run() builds the image, starts the sandbox, and waits until it is ready. The
image here is a slim Debian image with Python 3.12. Chalk caches each image it
builds, so starting another sandbox from the same image skips the build.

exec runs a process inside the sandbox and returns when the process exits.
stdout_text and stderr_text hold everything the process printed.

### Step 2: Run code that needs a package

An image can include Python packages. Start a second sandbox from an image that
installs NumPy, and run a line of Python in it:

```
numpy_sandbox = chalkcompute.Sandbox(
    image=chalkcompute.Image.debian_slim("3.12").pip_install(["numpy"]),
    name="tutorial-numpy",
).run()

result = numpy_sandbox.exec(
    "python3", "-c", "import numpy; print(numpy.arange(10).sum())"
)
print(result.stdout_text)
```

```
45
```

The first time Chalk builds an image, run() waits for the build, which can
take a few minutes.

The first sandbox is still running. Terminate both, since the next steps start
new ones:

```
sandbox.terminate()
numpy_sandbox.terminate()
```

When a sandbox terminates, its filesystem is deleted with it. To keep a file
after the sandbox is gone, save it to a volume.

### Step 3: Save a file to a volume

Create a volume named tutorial-notes by uploading a file to it from your
machine:

```
volume = chalkcompute.Volume("tutorial-notes")
volume.put_file("from-my-machine.txt", b"Uploaded from my machine\n")
```

Volume("tutorial-notes") does not contact Chalk by itself. The volume is
created the first time you read or write through it, here with put_file.

Now start a sandbox with the volume mounted at /data, and write a second file
from inside it. Inside the sandbox, the volume is an ordinary directory:

```
writer = chalkcompute.Sandbox(
    image=chalkcompute.Image.debian_slim("3.12"),
    name="tutorial-writer",
    volumes=[("tutorial-notes", "/data")],
).run()

writer.exec("sh", "-c", "echo 'Saved by tutorial-writer' > /data/note.txt")
result = writer.exec("/data/.chalk-volume/commit")
print(result.stdout_text)
writer.terminate()
```

```
published Chalk Volume version 2
```

Writes to a mounted volume stay inside the sandbox until they are committed.
/data/.chalk-volume/commit commits them and waits until the volume has saved
them as a new version. A sandbox also commits its writes when it terminates, but
that final commit has a time limit, so commit before you terminate a sandbox
whose writes you need. For the other ways to commit, see
Syncing a mounted volume.

### Step 4: Read the files from a second sandbox

The writer is gone, and its file is still on the volume. Start a new sandbox
with the same volume mounted, and read both files:

```
reader = chalkcompute.Sandbox(
    image=chalkcompute.Image.debian_slim("3.12"),
    name="tutorial-reader",
    volumes=[("tutorial-notes", "/data")],
).run()

result = reader.exec("sh", "-c", "cat /data/from-my-machine.txt /data/note.txt")
print(result.stdout_text)
```

```
Uploaded from my machine
Saved by tutorial-writer
```

Any number of sandboxes can mount the same volume. Each one sees the volume's
latest committed version when it starts.

### Step 5: Clean up

Terminate the reader and delete the volume:

```
reader.terminate()
volume.delete()
```

delete() removes the volume and every version of it. The sandboxes from the
earlier steps were terminated when you finished with them, so nothing from this
tutorial is left running.

### Next steps

- Configuring sandboxes: CPU, memory,
GPUs, environment variables, and secrets
- Running commands: stream output with
exec_stream, or send input with exec_start
- Networking: control what a sandbox can reach
- Long-running sandboxes: keep a
sandbox running for hours or days and reattach to it by name
- Volumes: upload files from your machine, read past
versions, and fork a volume




