# Containers
source: https://docs.chalk.ai/docs/compute/containers

## Run a single managed container with image building, file uploads, and exec-style command invocation.

A Container is one rung above a Sandbox: same
gVisor-isolated runtime underneath, but wrapped with image building, local file
upload, secret injection, and an exec-style invocation API. Use a container
for one-off batch jobs, interactive sessions, or long-running processes that
don't need replication.

If you need replicated HTTP-fronted workloads with autoscaling, use a
Scaling Group instead. If you need a serverless,
fan-out invocation model with a Python function as the interface, use
Functions.

### Quick start

```
from chalkcompute import Container, Image

c = Container(
    image=Image.debian_slim("3.12").pip_install(["requests"]),
    name="hello-container",
    cpu="1",
    memory="2Gi",
).run()

result = c.exec("python", "-c", "import requests; print(requests.__version__)")
print(result.stdout_text)
print(f"exit code: {result.exit_code}")

c.stop()
```

Container(...).run() does three things in sequence:

- Builds the Image (or skips if the spec is already
cached, since images are content-addressed).
- Uploads any local files declared with add_local_file / add_local_dir to
a volume and mounts it into the container.
- Creates the container and polls until it reaches the Running state.

.run() returns self, so you can chain it.

### Configuring resources

CPU, memory, and GPU are passed at construction time and follow Kubernetes
conventions:

```
c = Container(
    image=Image.debian_slim("3.12"),
    name="batch-job",
    cpu="4",           # cores, e.g. "500m" for half a core
    memory="16Gi",     # Mi / Gi suffixes
    gpu="nvidia-l4:1", # type:count, or just count
).run()
```

If you omit these flags, the service applies defaults from your environment's
resource configuration. GPU support and the set
of available GPU types depend on the node pools provisioned in your cluster —
see Sandbox § GPU.

### Environment variables and secrets

Inject configuration via env (plaintext) or secrets (resolved from Chalk's
managed secret store, an integration, or your local environment):

```
from chalkcompute import Container, Image, Secret

c = Container(
    image=Image.debian_slim("3.12"),
    name="api-client",
    env={"LOG_LEVEL": "INFO"},
    secrets=[
        Secret.from_env("OPENAI_API_KEY"),
        Secret.from_integration("prod_postgres"),
    ],
).run()
```

See the Secrets section of the Compute overview
for the full list of Secret constructors.

### Mounting volumes

Attach a Volume to share durable state between runs or
expose a large dataset without baking it into the image:

```
from chalkcompute import Container, Image, Volume

vol = Volume("training-data")
vol.put_file("inputs/example.txt", b"hello\n")

c = Container(
    image=Image.debian_slim("3.12"),
    name="trainer",
    volumes=[("training-data", "/data")],
).run()

result = c.exec("cat", "/data/inputs/example.txt")
print(result.stdout_text)  # "hello\n"
```

Volumes mounted this way are persistent across container restarts and shared
across other containers that mount the same volume.

### Executing commands

Container.exec(*command, timeout_secs=...) runs a one-shot process inside the
already-started container, blocking until the command finishes:

```
result = c.exec("ls", "-la", "/app")
print(result.stdout_text)
print(result.stderr_text)
print(result.exit_code)
```

exec captures stdout and stderr in full and returns them at the end. If you
need streaming output, stdin, or signal handling, drop down to the lower-level
Sandbox API — Container is a wrapper over the
same primitive.

### Exposing a port

If your container runs a long-lived server (HTTP, gRPC, etc.) that you want to
reach from inside the cluster, pass port:

```
c = Container(
    image=Image.debian_slim("3.12").pip_install(["flask"]),
    name="api",
    port=8080,
    entrypoint=["python", "-m", "flask", "run", "--host=0.0.0.0", "--port=8080"],
).run()
```

For externally addressable, replicated, autoscaling HTTP services, prefer a
Scaling Group — it gives you a public DNS name,
TLS termination, and replica management out of the box.

### Lifecycle management

### Attaching to an existing container

```
c = Container.from_name("hello-container")
# or
c = Container.from_id("550e8400-e29b-41d4-a716-446655440000")
```

This is useful for reconnecting to a long-lived container from a different
process without re-running .run() (which would attempt to recreate it).

### Inspecting status

```
c.refresh()      # re-fetch from the server
print(c.info.status)  # 'Running', 'Failed', etc.
print(c.id)
print(c.image_uri)
```

### Stopping

```
c.stop()                          # immediate stop + cleanup
c.stop(grace_period_seconds=30)   # SIGTERM, wait 30s, then SIGKILL
```

stop() also removes any temporary volumes created from add_local_file
uploads and deletes any secrets that were upserted from your local environment
for this run (e.g. via Secret.from_local_env).

### When to use a Container vs other compute primitives

See Choosing the right primitive
on the Compute overview for the full decision table.





