# Custom images

> Bring your own dependencies by registering a custom image as a template, from any public registry image, a Dockerfile, or the declarative FlaxImage builder.

The built-in templates (`python`, `node`, `blank`) cover a lot, but most real projects need extra
packages, a specific language version, or system libraries. A **custom image** is a reusable
[template](./concepts.md#templates) you register once and then create sandboxes from by name. There
are three ways to make one, all backed by the same template system.

## From a registry image

Point at any public registry image. Flax pulls it, pins it by digest, and marks the template
`ready`.

```python
from flaxcloud import FlaxClient

flax = FlaxClient()

tpl = flax.create_template("my-python", image="python:3.12-slim")  # blocks until ready
print(tpl.status)  # "ready"

sb = flax.create_sandbox(template="my-python")  # by name (or tpl.id)
print(sb.run("python3 --version").stdout)
```

```ts
const tpl = await flax.createTemplate("my-python", { image: "python:3.12-slim" });
const sb = await flax.createSandbox({ template: "my-python" });
```

```bash
flax template create my-python --image python:3.12-slim
flax sandbox create --template my-python
```

Pass `wait=False` (Python) / `{ wait: false }` (JS) to return immediately and poll with
`get_template(id)` / `waitForTemplate(id)` yourself.

## From a Dockerfile

When Dockerfile builds are enabled on the server, build an image from a Dockerfile string and
stream the build log live.

```python
df = "FROM python:3.12-slim\nRUN pip install pandas numpy"
tpl = flax.create_template("data-img", dockerfile=df, wait=False)
for line in flax.build_logs(tpl.id):   # live build output
    print(line, end="")
tpl = flax.get_template(tpl.id)        # status is now "ready" or "failed"
```

```bash
flax template create data-img --dockerfile ./Dockerfile   # streams the build log
```

Identical Dockerfiles reuse a cached image, so repeat builds are fast.

## With the declarative builder

Rather than hand-writing a Dockerfile, describe the image with `FlaxImage` and let the SDK generate
one. It uses the same Dockerfile build flow under the hood, so build status, logs, and `wait`
behave identically.

```python
from flaxcloud import FlaxImage

image = (
    FlaxImage.python("3.12")
    .apt_install(["git", "ffmpeg"])
    .pip_install(["requests", "beautifulsoup4"])
    .env({"PYTHONUNBUFFERED": "1"})
    .workdir("/workspace")
)
print(image.to_dockerfile())   # inspect the generated Dockerfile

tpl = flax.create_template_from_image_definition("scraper-agent", image, wait=True)
sb = flax.create_sandbox(template="scraper-agent")
```

```ts
import { FlaxImage } from "@flaxcloud/sdk";

const image = FlaxImage.python("3.12")
  .aptInstall(["git"])
  .pipInstall(["requests"])
  .workdir("/workspace");
const tpl = await flax.createTemplateFromImage("scraper", image);
const sb = await flax.createSandbox({ template: tpl.name });
```

Constructors: `base(image)`, `python(version)`, `node(version)`, `debian_slim()`. Chainable steps:
`apt_install`, `pip_install`, `npm_install`, `run`, `env`, `workdir`, `copy_file`, `copy_dir`. Each
package and argument is single-quoted and validated, so version specifiers like `"requests>=2,<3"`
are safe; use `.run(...)` for anything custom. `copy_file`/`copy_dir` need a local build context,
which the hosted builder does not accept yet, so use `.run(...)` (curl/git) to fetch files for now.

## Manage templates

```python
for t in flax.list_templates():     # includes built-ins (blank/python/node)
    print(t.name, t.source_type, t.status)

flax.delete_template(tpl.id)
```

```bash
flax template ls
flax template get tpl_abc123
flax template rm  tpl_abc123
```

## Limits

Custom images count against your plan: `max_custom_templates` and `max_image_size_mb`. See
[Plans & limits](./plans-and-limits.md). For the full SDK surface, see the
[Python SDK](./sdk.md#custom-images-templates) and
[JavaScript SDK](./sdk-js.md#custom-images) pages.
