# Images

An image is the filesystem a sandbox boots from, together with the environment variables, working directory, entrypoint, and command the sandbox starts with. Daytona accepts standard OCI images, the same ones you build and push with Docker, and converts each one into a boot-optimized format when you register it. Images belong to a project, and a sandbox references its image by name or ID. A sandbox create does not pull from a registry, so a sandbox boots only from an image already registered in the project.

Every sandbox created from an image starts with the same filesystem and configuration. An image holds no running processes or memory. A [snapshot](https://www.daytona.io/docs/en/v2/snapshots.md) saves the memory and disk of a running sandbox, and a restored sandbox resumes its processes.

| **Concept**            | **Description**                                                                                     |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| **Registry image**     | An image pulled from a public or private registry and registered under a name.                      |
| **Dockerfile build**   | An image Daytona builds from Dockerfile text, with or without a build context.                      |
| **Capture**            | An image made from the filesystem of a running Linux sandbox.                                       |
| **Anonymous image**    | An image without a name. Daytona deletes it after it goes unused for its idle window.               |
| **Status**             | **`pending`**, **`building`**, **`ready`**, or **`failed`**. Sandboxes boot only from a **`ready`** image.              |
| **Home region**        | The region where Daytona builds, pulls, or captures the image.                                      |

## Use cases

Images are used when many sandboxes need the same software, files, or configuration.

- **Preinstalled dependencies**: packages, language runtimes, and tools are installed once in a Dockerfile build, and every sandbox from the image starts with them.
- **Shared environments**: a named image gives every sandbox in a project the same filesystem and start configuration under one name.
- **Pinned environments**: a registry image is registered under a name, so a moved or deleted registry tag does not change what sandboxes boot.
- **Configured sandboxes as templates**: a sandbox set up by hand or by an agent is captured, and new sandboxes start from its filesystem.
- **Private application images**: images that a CI pipeline pushes to a private registry are imported with the project's [registry credentials](https://www.daytona.io/docs/en/v2/registries.md) and boot in sandboxes.
- **Environments written by agents**: an agent writes a Dockerfile, builds it, and boots sandboxes from the result, without access to your build infrastructure.
- **amd64 and arm64 builds**: one Dockerfile builds for either CPU architecture, so sandboxes on both kinds of host run the same software.
- **Evaluation and reinforcement learning (RL)**: each test case or episode starts in a new sandbox from the same image, so results do not depend on another run.

## Create sandboxes from images

Create a sandbox from an image registered in the project, by its name or ID. A sandbox create does not pull from a registry, so the image is [pulled](#pull-images), [built](#build-images), or [captured](#capture-images) first. Registration runs in the background, and a create from an image that is not ready yet fails.


```python
from daytona import Daytona

daytona = Daytona()

# Register python:3.12-slim under the name python-3.12
image = daytona.v2.images.pull("python:3.12-slim", name="python-3.12")

# Each wait returns after 30 seconds at most; a create from an image that is not ready fails
while image.status not in ("ready", "failed"):
    image = daytona.v2.images.get(image.id, wait="ready")
if image.status == "failed":
    raise RuntimeError(image.error)

sandbox = daytona.v2.create("python-3.12")
```

- **`images.get(id, wait="ready")`** returns when the image is **`ready`** or **`failed`**, or when the server wait expires.
- Set **`timeout`** in seconds to change the server wait, up to 60.
- **`images.get()`** does not raise on **`failed`** or on expiry, so check **`status`** and **`error`** on the returned image.
- A value that contains **`/`** or **`:`** is treated as a registry reference.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

// Register python:3.12-slim under the name python-3.12
let image = await daytona.v2.images.pull('python:3.12-slim', { name: 'python-3.12' })

// Each wait returns after 30 seconds at most; a create from an image that is not ready fails
while (image.status !== 'ready' && image.status !== 'failed') {
  image = await daytona.v2.images.get(image.id, { wait: 'ready' })
}
if (image.status === 'failed') throw new Error(image.error ?? 'image build failed')

const sandbox = await daytona.v2.create('python-3.12')
```

- **`images.get(id, { wait: 'ready' })`** returns when the image is **`ready`** or **`failed`**, or when the server wait expires.
- Set **`timeout`** in seconds to change the server wait, up to 60.
- **`images.get()`** does not throw on **`failed`** or on expiry, so check **`status`** and **`error`** on the returned image.
- A value that contains **`/`** or **`:`** is treated as a registry reference.


```bash
# Register python:3.12-slim under the name python-3.12; waits until the image is ready
daytona image create python-3.12 --ref python:3.12-slim

daytona sandbox create --image python-3.12
```

- A value that contains **`/`** or **`:`** is treated as a registry reference.


```bash
# Register python:3.12-slim under the name python-3.12; the response carries its id
curl 'https://api.daytona.com/images' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "name": "python-3.12", "source": { "type": "registry", "ref": "python:3.12-slim" } }'

# Returns on ready or failed, or after timeout_ms; repeat while status is pending or building
curl 'https://api.daytona.com/images/{imageId}?wait=ready&timeout_ms=60000' \
  --header 'Authorization: Bearer YOUR_API_KEY'

curl 'https://api.daytona.com/sandboxes' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "image": "python-3.12" }'
```

- **`GET /images/{imageId}?wait=ready`** answers **`200`** when the image is **`ready`** or **`failed`**, or when **`timeout_ms`** expires.
- The default **`timeout_ms`** is **`30000`**, and the maximum is **`60000`**.
- Check **`status`** in the response.
- A value that contains **`/`** or **`:`** is treated as a registry reference.


### Inline Dockerfile

Build an image from Dockerfile text and create a sandbox from it. The SDKs do both in one call. The image is anonymous: it has no name, and the default image list does not show it. Daytona deletes an anonymous image once it goes unused for its idle window, the server maximum of 7 days by default; every sandbox create from the image restarts the window.

The Dockerfile must be self-contained. There is no build context, so instructions that copy local files have nothing to copy. Use a [build context](#build-context) for those.


```python
from daytona import Daytona
from daytona.v2 import ImageBuilder

daytona = Daytona()

# Builds an anonymous image from the Dockerfile, then creates the sandbox from it
sandbox = daytona.v2.create_from_build(
    ImageBuilder.from_dockerfile("FROM python:3.12-slim\nRUN pip install requests\n")
)
```

| **Parameter** | **Type** | **Description**                                                                                                                                         |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`wait`** | **`int`** | Sets one time budget in seconds for the build and the create together. A build that runs longer fails with **`408`** **`REQUEST_TIMEOUT`**. Raise the value for longer builds, or pass **`0`** to remove the limit. The default is **`60`**. |
| **`idle`** | **`int`** | Sets the idle window of the image in seconds. It applies only to the image. |
| **`region`** | **`str`** | Selects the region for both the build and the sandbox. |

- **`cpu_arch`** also applies to both the build and the sandbox.
- Every other keyword goes to **`create`**.
- A failed build creates no sandbox.


```typescript
import { Daytona } from '@daytona/sdk'
import { ImageBuilder } from '@daytona/sdk/v2'

const daytona = new Daytona()

// Builds an anonymous image from the Dockerfile, then creates the sandbox from it
const sandbox = await daytona.v2.createFromBuild(
  ImageBuilder.fromDockerfile('FROM python:3.12-slim\nRUN pip install requests\n'),
)
```

| **Parameter** | **Type** | **Description**                                                                                                                                         |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`wait`** | **`number`** | Sets one time budget in seconds for the build and the create together. A build that runs longer fails with **`408`** **`REQUEST_TIMEOUT`**. Raise the value for longer builds, or pass **`0`** to remove the limit. The default is **`60`**. |
| **`idle`** | **`number`** | Sets the idle window of the image in seconds. It applies only to the image. |
| **`region`** | **`string`** | Selects the region for both the build and the sandbox. |

- **`cpuArch`** also applies to both the build and the sandbox.
- A failed build creates no sandbox.


```bash
# Build an anonymous image from the inline Dockerfile; the response carries its id
curl 'https://api.daytona.com/images' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "dockerfile": "FROM python:3.12-slim\nRUN pip install requests\n" }'

# Returns on ready or failed, or after timeout_ms; repeat while status is pending or building
curl 'https://api.daytona.com/images/{imageId}?wait=ready&timeout_ms=60000' \
  --header 'Authorization: Bearer YOUR_API_KEY'

# Create the sandbox from the image id
curl 'https://api.daytona.com/sandboxes' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "image": "{imageId}" }'
```

| **Field** | **Type**  | **Description**                                                                                                                                       |
| --------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`dockerfile`** | **`string`** | Sets the Dockerfile text. The Dockerfile must not copy local files. This field cannot be sent together with **`source`**. This field is required. |
| **`name`** | **`string`** | Sets the image name. Without a name, the image is anonymous. |
| **`idle`** | **`integer`** | Sets the idle window of the image in seconds. Omit the field or pass **`-1`** for the server maximum. Otherwise, pass a value from **`1`** up to the maximum. Other values return **`400`** **`IDLE_OUT_OF_RANGE`**. |


## Pull images

Pull an image from a registry and register it under a name. The pull runs in the background, and the call returns once it is queued. Pulling again under the name of an existing registry or Dockerfile image re-imports the image: it returns to pending, and sandbox creates from it fail until the new pull is ready. A private registry authenticates with the [registry credential](https://www.daytona.io/docs/en/v2/registries.md) stored for the image host.


```python
from daytona import Daytona

daytona = Daytona()

image = daytona.v2.images.pull("ghcr.io/acme/agent:v3", name="my-awesome-image")
```

| **Parameter** | **Type** | **Description**                       |
| ------------- | -------- | ------------------------------------- |
| **`name`** | **`str`** | Sets the image name. The name is 1 to 128 characters of letters, digits, **`.`**, **`_`**, and **`-`**. It must not start with **`.`**, **`snap-`**, **`build-`**, **`refs`**, **`by-digest`**, or **`prj-`**. It must not look like an image ID, **`img_...`**. Images and snapshots share one name space per project. A name that belongs to a captured image or a snapshot returns **`409`**. Without a name, the image is anonymous. An anonymous pull reuses an existing pending, building, or ready anonymous image of the same reference in the same region. |

- Wait with **`images.get(image.id, wait="ready")`** before creating sandboxes.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

const image = await daytona.v2.images.pull('ghcr.io/acme/agent:v3', { name: 'my-awesome-image' })
```

| **Parameter** | **Type** | **Description**                       |
| ------------- | -------- | ------------------------------------- |
| **`name`** | **`string`** | Sets the image name. The name is 1 to 128 characters of letters, digits, **`.`**, **`_`**, and **`-`**. It must not start with **`.`**, **`snap-`**, **`build-`**, **`refs`**, **`by-digest`**, or **`prj-`**. It must not look like an image ID, **`img_...`**. Images and snapshots share one name space per project. A name that belongs to a captured image or a snapshot returns **`409`**. Without a name, the image is anonymous. An anonymous pull reuses an existing pending, building, or ready anonymous image of the same reference in the same region. |

- Wait with **`images.get(image.id, { wait: 'ready' })`** before creating sandboxes.


```bash
daytona image create my-awesome-image --ref ghcr.io/acme/agent:v3
```

| **Flag** | **Type** | **Description** |
| -------- | -------- | --------------- |
| **`--ref`** | **`string`** | Sets the registry reference to pull. This flag is required. |
| **`--timeout`** | **`string`** | Sets how long the command waits for the pull to finish. The default is **`10m`**. |
| **`--no-wait`** | **`boolean`** | Returns once the pull is queued, without waiting for it to finish. |

- The command exits with an error if the pull fails.
- The name is 1 to 128 characters of letters, digits, **`.`**, **`_`**, and **`-`**.
- The name must not start with **`.`**, **`snap-`**, **`build-`**, **`refs`**, **`by-digest`**, or **`prj-`**.
- The name must not look like an image ID, **`img_...`**.
- Images and snapshots share one name space per project.
- A name that belongs to a captured image or a snapshot returns **`409`**.


```bash
curl 'https://api.daytona.com/images' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-awesome-image",
  "source": { "type": "registry", "ref": "ghcr.io/acme/agent:v3" }
}'
```

- **`POST /images`** returns **`202`** with the image **`id`** and **`status`** once the pull is queued.
- Wait with **`GET /images/{imageId}?wait=ready`** before creating sandboxes.
- The name is 1 to 128 characters of letters, digits, **`.`**, **`_`**, and **`-`**.
- The name must not start with **`.`**, **`snap-`**, **`build-`**, **`refs`**, **`by-digest`**, or **`prj-`**.
- The name must not look like an image ID, **`img_...`**.
- Images and snapshots share one name space per project.
- A name that belongs to a captured image or a snapshot returns **`409`**.


## Build images

Build an image from a Dockerfile and register it under a name. The build runs in the background: the call returns once it is queued, and the image can be used once it is ready.

Builds are deduplicated within a project and region. Submitting the same Dockerfile and build context for the same CPU architecture reuses the earlier build instead of running a new one; if that build already finished, the new image is ready at once.


```python
from daytona import Daytona
from daytona.v2 import ImageBuilder

daytona = Daytona()

image = daytona.v2.images.build(
    ImageBuilder.from_dockerfile("FROM python:3.12-slim\nRUN pip install requests"),
    name="my-awesome-image",
)
```

| **Parameter** | **Type** | **Description**                       |
| ------------- | -------- | ------------------------------------- |
| **`name`** | **`str`** | Sets the image name. Without a name, the image is anonymous. |
| **`idle`** | **`int`** | Sets how many seconds an anonymous image can go without a sandbox create before deletion. It applies only to anonymous images. **`-1`** sets the maximum. The default is the server maximum. |

- **`images.build`** takes an **`ImageBuilder`**, not a raw Dockerfile string.
- Wait with **`images.get(image.id, wait="ready")`** before use.


```typescript
import { Daytona } from '@daytona/sdk'
import { ImageBuilder } from '@daytona/sdk/v2'

const daytona = new Daytona()

const image = await daytona.v2.images.build(
  ImageBuilder.fromDockerfile('FROM python:3.12-slim\nRUN pip install requests'),
  { name: 'my-awesome-image' },
)
```

| **Parameter** | **Type** | **Description**                       |
| ------------- | -------- | ------------------------------------- |
| **`name`** | **`string`** | Sets the image name. Without a name, the image is anonymous. |
| **`idle`** | **`number`** | Sets how many seconds an anonymous image can go without a sandbox create before deletion. It applies only to anonymous images. **`-1`** sets the maximum. The default is the server maximum. |

- Wait with **`images.get(image.id, { wait: 'ready' })`** before use.


```bash
# The CLI uploads a build context; write the Dockerfile into it first
mkdir -p build && printf 'FROM python:3.12-slim\nRUN pip install requests\n' > build/Dockerfile

# Waits until the image is ready
daytona image build my-awesome-image --context build
```

- The command waits up to **`--timeout`** and exits with an error if the build fails.
- The default **`--timeout`** is **`10m`**.
- **`--no-wait`** returns once the build is queued.


```bash
curl 'https://api.daytona.com/images' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-awesome-image",
  "dockerfile": "FROM python:3.12-slim\nRUN pip install requests"
}'
```

| **Field**    | **Type** | **Description**                                                                                                                               |
| ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **`dockerfile`** | **`string`** | Sets the Dockerfile text. The Dockerfile must not copy local files. To send a build context, use the multipart form in [build context](#build-context). |
| **`name`** | **`string`** | Sets the image name. Without a name, the image is anonymous. |

The response has the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| **`id`** | Identifies the new image. |
| **`status`** | The status is **`pending`** for a new build. It is **`building`** when the request joined a build in progress, and **`ready`** when the request reused a finished build. |


### Build context

Build an image from a Dockerfile and a build context. Daytona uploads the context with the request and runs the build in the image's home region. A build context is needed when the Dockerfile copies local files. Without one, the Dockerfile is sent as text, as in [build images](#build-images).


```bash
# The context holds the Dockerfile at its root
daytona image build my-awesome-image --context context.tar
```

| **Flag**       | **Type** | **Description**                                                    |
| -------------- | -------- | ------------------------------------------------------------------ |
| **`--context`** | **`string`** | Sets the build context, a tar archive or a directory. The packed context is at most 64 MiB. This flag is required. |
| **`--dockerfile`** | **`string`** | Sets the path of the Dockerfile inside the context. The default is **`Dockerfile`**. |
| **`--timeout`** | **`string`** | Sets how long the command waits for the build to finish. The default is **`10m`**. |
| **`--no-wait`** | **`boolean`** | Returns once the build is queued, without waiting for it to finish. |

- The command exits with an error if the build fails.


```bash
curl 'https://api.daytona.com/images' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --form 'name=my-awesome-image' \
  --form 'context=@context.tar'
```

| **Field**    | **Type** | **Description**                                                                                              |
| ------------ | -------- | ------------------------------------------------------------------------------------------------------------ |
| **`context`** | **`file`** | Sets the build context as a tar archive, at most 64 MiB. This field is required. |
| **`name`** | **`string`** | Sets the image name. Without a name, the image is anonymous. |
| **`dockerfile`** | **`string`** | Sets the path of the Dockerfile inside the context. The default is **`Dockerfile`**. |
| **`region`** | **`string`** | Sets the home region of the build. The default is the resolved default region. |
| **`cpu_arch`** | **`string`** | Sets the CPU architecture, **`amd64`** or **`arm64`**. The default is the region's architecture. |
| **`idle`** | **`integer`** | Sets how many seconds an anonymous image can go without a sandbox create before deletion. It applies only to anonymous images. The default is the server maximum. |

- The call returns once the build is queued.
- Wait with **`GET /images/{imageId}?wait=ready`** before creating sandboxes.


### Build options

[Build](#build-images) accepts an optional home region and CPU architecture. [Pull](#pull-images) accepts only a home region and always uses the region's architecture. The same Dockerfile built for two architectures produces two separate images.


```python
from daytona import Daytona
from daytona.v2 import ImageBuilder

daytona = Daytona()

image = daytona.v2.images.build(
    ImageBuilder.from_dockerfile("FROM python:3.12-slim\nRUN pip install requests"),
    name="my-awesome-image",
    region="us",
    cpu_arch="arm64",
)
```

| **Parameter** | **Type** | **Description**                                                                                                      |
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| **`region`** | **`str`** | Sets the home region of the image. The default is the resolved default region for the project, organization, or platform. |
| **`cpu_arch`** | **`str`** | Sets the CPU architecture to build for, **`amd64`** or **`arm64`**. The pull methods accept only **`region`**, not **`cpu_arch`**. A **`from_registry`** builder with no steps is also a pull, so **`cpu_arch`** does not apply to that builder either. The default is the region's architecture. |


```typescript
import { Daytona } from '@daytona/sdk'
import { ImageBuilder } from '@daytona/sdk/v2'

const daytona = new Daytona()

const image = await daytona.v2.images.build(
  ImageBuilder.fromDockerfile('FROM python:3.12-slim\nRUN pip install requests'),
  { name: 'my-awesome-image', region: 'us', cpuArch: 'arm64' },
)
```

| **Parameter** | **Type**  | **Description**                                                                                            |
| ------------- | --------- | ---------------------------------------------------------------------------------------------------------- |
| **`region`** | **`string`** | Sets the home region of the image. The default is the resolved default region for the project, organization, or platform. |
| **`cpuArch`** | **`CpuArch`** | Sets the CPU architecture to build for, **`amd64`** or **`arm64`**. **`images.pull`** also accepts **`cpuArch`**, but a pull ignores the value and uses the region's architecture. A **`fromRegistry`** builder with no steps ignores the value the same way. The default is the region's architecture. |


```bash
# build/ holds the Dockerfile from Build images; cpu_arch is read from the JSON request body on stdin
echo '{ "cpu_arch": "arm64" }' | daytona image build my-awesome-image \
  --context build \
  --region us \
  --file -
```

| **Flag**   | **Type** | **Description**                                                                                                         |
| ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
| **`--region`** | **`string`** | Sets the home region of the image. The default is the resolved default region for the project, organization, or platform. |
| **`--file`** | **`string`** | Reads the JSON request body from a file, or from stdin with **`-`**. The **`cpu_arch`** field of the body sets the CPU architecture, **`amd64`** or **`arm64`**. Without **`cpu_arch`**, the image is built for the region's architecture. |


```bash
curl 'https://api.daytona.com/images' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-awesome-image",
  "dockerfile": "FROM python:3.12-slim\nRUN pip install requests",
  "region": "us",
  "cpu_arch": "arm64"
}'
```

| **Field**  | **Type** | **Description**                                                                                            |
| ---------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| **`region`** | **`string`** | Sets the home region of the image. The default is the resolved default region for the project, organization, or platform. |
| **`cpu_arch`** | **`string`** | Sets the CPU architecture to build for, **`amd64`** or **`arm64`**. The default is the region's architecture. |


### Declarative builds

Describe an image in code instead of writing a Dockerfile. Start from a registry image, chain the steps the image needs, and build it like any other image. Each step adds one Dockerfile instruction, and the SDK renders the Dockerfile when the build is submitted.

Both SDKs render the same Dockerfile for the same steps, so a build submitted from Python and the same build submitted from TypeScript are [deduplicated](#build-images) into one image. A sandbox booted from the image gets its environment variables and runs its entrypoint and command in its working directory, or in the user's home directory when the image sets none or sets the root directory. The image's user does not carry over: sandboxes run as root unless a user is set when the sandbox is created.


```python
from daytona import Daytona
from daytona.v2 import ImageBuilder

daytona = Daytona()

builder = (
    ImageBuilder.from_registry("python:3.12-slim")
    .pip_install("requests", "numpy>=1.26")
    .run_commands("mkdir -p /app/data")
    .env({"LOG_LEVEL": "debug"})
    .workdir("/app")
)

image = daytona.v2.images.build(builder, name="my-awesome-image")
```

| **Method**      | **Type**                                   | **Description**                                                                                                                                       |
| --------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`from_registry`** | **`str`** | Sets the registry image reference to start from, rendered as **`FROM`**. A builder from **`from_registry`** with no steps is a registry import, not a build. **`images.build`** pulls that image as [pull images](#pull-images) does. **`from_dockerfile`** starts from Dockerfile text instead, and the steps are appended after that text. This method is required unless the builder starts from **`from_dockerfile`**. |
| **`pip_install`** | **`*str`** | Adds Python packages as one **`RUN python -m pip install`** step. Each package is shell-quoted, so a version specifier such as **`numpy>=1.26`** stays one argument. |
| **`run_commands`** | **`*str`** | Adds shell commands as one **`RUN`** step per command. |
| **`env`** | **`str, str`** or **`Mapping[str, str]`** | Sets one variable as a name and a value, or several as a mapping. Each variable is rendered as one **`ENV`** step. |
| **`workdir`** | **`str`** | Sets the working directory, rendered as **`WORKDIR`**. |

- Each method returns a new builder and leaves the original unchanged, so one base builder can start several images.


```typescript
import { Daytona } from '@daytona/sdk'
import { ImageBuilder } from '@daytona/sdk/v2'

const daytona = new Daytona()

const builder = ImageBuilder.fromRegistry('python:3.12-slim')
  .pipInstall('requests', 'numpy>=1.26')
  .runCommands('useradd --create-home app')
  .env({ LOG_LEVEL: 'debug' })
  .user('app')
  .workdir('/home/app')
  .entrypoint('python')
  .cmd('-m', 'http.server', '8080')

const image = await daytona.v2.images.build(builder, { name: 'my-awesome-image' })
```

| **Method**     | **Type**                                     | **Description**                                                                                                                                       |
| -------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`fromRegistry`** | **`string`** | Sets the registry image reference to start from, rendered as **`FROM`**. A builder from **`fromRegistry`** with no steps is a registry import, not a build. **`images.build`** pulls that image as [pull images](#pull-images) does. **`fromDockerfile`** starts from Dockerfile text instead, and the steps are appended after that text. This method is required unless the builder starts from **`fromDockerfile`**. |
| **`pipInstall`** | **`...string[]`** | Adds Python packages as one **`RUN python -m pip install`** step. Each package is shell-quoted, so a version specifier such as **`numpy>=1.26`** stays one argument. |
| **`runCommands`** | **`...string[]`** | Adds shell commands as one **`RUN`** step per command. |
| **`env`** | **`string, string`** or **`Record<string, string>`** | Sets one variable as a name and a value, or several as a record. Each variable is rendered as one **`ENV`** step. |
| **`user`** | **`string`** | Sets the user for the build steps that follow, rendered as **`USER`**. Sandboxes do not run as this user. This method is available only in the TypeScript SDK. |
| **`workdir`** | **`string`** | Sets the working directory, rendered as **`WORKDIR`**. |
| **`entrypoint`** | **`...string[]`** | Sets the image entrypoint, rendered as **`ENTRYPOINT`** in exec form. This method is available only in the TypeScript SDK. |
| **`cmd`** | **`...string[]`** | Sets the image command, rendered as **`CMD`** in exec form. With an entrypoint, the **`cmd`** values are the default arguments of the entrypoint. This method is available only in the TypeScript SDK. |

- Each method returns a new builder and leaves the original unchanged, so one base builder can start several images.


### Preview the Dockerfile

Render the Dockerfile a builder describes and the request body it produces, without submitting a build. Use it to review the generated instructions, or to send the build yourself through the API.


```python
from daytona.v2 import ImageBuilder

builder = ImageBuilder.from_registry("python:3.12-slim").pip_install("requests")

print(builder.render_dockerfile())
# FROM python:3.12-slim
# RUN python -m pip install requests

source, dockerfile = builder.build_request_source()
```

- **`build_request_source`** returns the two **`POST /images`** fields as a tuple, and one of the two is **`None`**.
- For a registry import with no steps, **`source`** holds **`{"type": "registry", "ref": ...}`**.
- For any other builder, **`dockerfile`** holds the rendered text.


```typescript
import { ImageBuilder } from '@daytona/sdk/v2'

const builder = ImageBuilder.fromRegistry('python:3.12-slim').pipInstall('requests')

console.log(builder.toDockerfile())
// FROM python:3.12-slim
// RUN python -m pip install requests

const source = builder.toRequestSource()
```

- **`toRequestSource`** returns the source part of a **`POST /images`** body.
- For a registry import with no steps, **`toRequestSource`** returns **`{ source: { type: 'registry', ref } }`**.
- For any other builder, **`toRequestSource`** returns **`{ dockerfile }`** with the rendered text.


## Capture images

Capture the filesystem of a running sandbox as an image. The sandbox keeps running, and new sandboxes boot from the image. The capture runs in the background: the image starts as pending and becomes ready when the capture finishes. Linux sandboxes can be captured; a macOS sandbox cannot be captured.

A named capture is permanent until you delete it. An unnamed capture is anonymous and is deleted once unused for its idle window, the server maximum of 7 days by default; every sandbox create from the image restarts the window. To keep an anonymous capture, [promote](#promote-images) it.


```python
from daytona import Daytona

daytona = Daytona()
sandbox = daytona.v2.create("python-3.12")
sandbox.process.run("pip install requests")

image = sandbox.snapshot_filesystem(name="my-env")

# Wait until the capture is ready, then boot new sandboxes from it
while image.status not in ("ready", "failed"):
    image = daytona.v2.images.get(image.id, wait="ready")
if image.status == "failed":
    raise RuntimeError(image.error)
clone = daytona.v2.create("my-env")
```

| **Parameter** | **Type** | **Description**                                                                                                             |
| ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`name`** | **`str`** | Sets the image name, which must be unique in the project. The default is **`None`**, which makes the image anonymous. |
| **`idle`** | **`int`** | Sets how many seconds an anonymous image can go without a sandbox create before deletion. It applies only to anonymous images. **`-1`** sets the maximum. The default is the server maximum. |

- **`snapshot_filesystem`** returns the pending **`Image`** and does not wait for the capture.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const sandbox = await daytona.v2.create('python-3.12')
await sandbox.process.run('pip install requests')

let image = await sandbox.snapshotFilesystem({ name: 'my-env' })

// Wait until the capture is ready, then boot new sandboxes from it
while (image.status !== 'ready' && image.status !== 'failed') {
  image = await daytona.v2.images.get(image.id, { wait: 'ready' })
}
if (image.status === 'failed') throw new Error(image.error ?? 'image build failed')
const clone = await daytona.v2.create('my-env')
```

| **Parameter** | **Type** | **Description**                                                                                                             |
| ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`name`** | **`string`** | Sets the image name, which must be unique in the project. The default is **`undefined`**, which makes the image anonymous. |
| **`idle`** | **`number`** | Sets how many seconds an anonymous image can go without a sandbox create before deletion. It applies only to anonymous images. **`-1`** sets the maximum. The default is the server maximum. |

- **`snapshotFilesystem`** resolves to the pending **`Image`** and does not wait for the capture.


```bash
# Named capture: permanent until deleted
daytona image capture {sandboxId} --name my-env

# Anonymous capture, deleted after one hour without a sandbox create
daytona image capture {sandboxId} --idle 3600
```

| **Flag**   | **Type** | **Description**                                                                                                             |
| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`--name`** | **`string`** | Sets the image name, which must be unique in the project. Without a name, the image is anonymous. |
| **`--idle`** | **`int`** | Sets how many seconds an anonymous image can go without a sandbox create before deletion. It applies only to anonymous images. **`-1`** sets the maximum. The default is the server maximum. |
| **`--timeout`** | **`string`** | Sets how long the command waits for the image to be ready. The default is **`10m`**. |
| **`--no-wait`** | **`boolean`** | Returns once the capture is queued, without waiting for the image to be ready. |

- The command exits with an error if the capture fails.


```bash
curl 'https://api.daytona.com/sandboxes/{sandboxId}/images' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{ "name": "my-env" }'
```

| **Field** | **Type**  | **Description**                                                                                                             |
| --------- | --------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`name`** | **`string`** | Sets the image name, which must be unique in the project. Without a name, the image is anonymous. |
| **`idle`** | **`integer`** | Sets how many seconds an anonymous image can go without a sandbox create before deletion. It applies only to anonymous images. **`-1`** sets the maximum. The default is the server maximum. |

- Wait with **`GET /images/{imageId}?wait=ready`** before using the image.
- The call requires the **`write:images`** permission.


## Promote images

Promote an anonymous image by giving it a name. A promoted image is permanent: it is no longer deleted after its idle window, and the default image list shows it. Promotion works only on anonymous images; an image that already has a name cannot be renamed.


```python
from daytona import Daytona

daytona = Daytona()

image = daytona.v2.images.promote("{imageId}", "my-env")
print(image.name)
```

| **Parameter** | **Type** | **Description**                                         |
| ------------- | -------- | ------------------------------------------------------- |
| **`image_id`** | **`str`** | Identifies the image by its ID, **`img_...`**. This parameter is required. |
| **`name`** | **`str`** | Sets the image name, which must be unique in the project. This parameter is required. |


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

const image = await daytona.v2.images.promote('{imageId}', 'my-env')
console.log(image.name)
```

| **Parameter** | **Type** | **Description**                                         |
| ------------- | -------- | ------------------------------------------------------- |
| **`imageId`** | **`string`** | Identifies the image by its ID, **`img_...`**. This parameter is required. |
| **`name`** | **`string`** | Sets the image name, which must be unique in the project. This parameter is required. |


```bash
daytona image promote {imageId} my-env
```


```bash
curl 'https://api.daytona.com/images/{imageId}' \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{ "name": "my-env" }'
```

| **Field** | **Type** | **Description**                                      |
| --------- | -------- | ---------------------------------------------------- |
| **`name`** | **`string`** | Sets the image name, which must be unique in the project. This field is required. |

- The call requires the **`write:images`** permission.


## Convert images

Convert an image to the current image format before its first use. Images registered by the migration from Daytona v0 are stored in an older format until they are converted. Such an image converts on its first Linux sandbox create, which waits in the queued state while the conversion runs. Builds that name the image as their source refuse it until it is converted. Converting ahead of time removes the wait from the first create and makes the image usable everywhere.


```bash
curl 'https://api.daytona.com/images/{imageId}/convert' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY'

# Returns on ready or failed, or after timeout_ms; repeat while status is pending or building
curl 'https://api.daytona.com/images/{imageId}?wait=ready&timeout_ms=60000' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

The response has the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| **`converted`** | The value is **`true`** when the call started a conversion or joined one in progress. The value is **`false`** when the image is already in the current format and nothing was queued. |

- The call takes the image ID, **`img_...`**.
- The call returns **`202`** with **`id`**, **`from_format`**, **`to_format`**, **`status`**, and **`converted`**.
- An image that needs conversion has **`format_outdated`** set to **`true`**.
- The call is safe to repeat. It joins a conversion in progress and retries a failed one.
- The call requires the **`write:images`** permission.
- Other sandbox classes and builds refuse an unconverted image with **`409`** **`IMAGE_FORMAT_OUTDATED`**.
- While the conversion runs, other sandbox classes and builds refuse the image with **`409`** **`IMAGE_NOT_READY`**.
- **`POST /images/{imageId}/convert`** returns **`409`** when the image records no source it can be converted from.


## Operations

### List images

List images in the project, newest first. The list returns named images by default. Anonymous images, such as the ones [inline Dockerfile](#inline-dockerfile) builds create, appear only when you ask for them.


```python
from daytona import Daytona

daytona = Daytona()

page = daytona.v2.images.list()
for image in page.items:
    print(image.name, image.status)
```

| **Parameter** | **Type**                  | **Description**                                                                   |
| ------------- | ------------------------- | --------------------------------------------------------------------------------- |
| **`anonymous`** | **`bool \| Literal["any"]`** | Selects which images to list. **`True`** returns anonymous images only, and **`"any"`** returns both named and anonymous images. The default is **`False`**. |
| **`limit`** | **`int`** | Sets the page size, clamped to **`1`** through **`200`**. The default is **`100`**. |

- **`page.next_cursor`** is set when more images exist.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

const page = await daytona.v2.images.list()
for (const image of page.items) {
  console.log(image.name, image.status)
}
```

| **Parameter** | **Type**                          | **Description**                                                     |
| ------------- | --------------------------------- | ------------------------------------------------------------------- |
| **`anonymous`** | **`'true' \| 'false' \| 'any'`** | Selects which images to list. **`'true'`** returns anonymous images only, and **`'any'`** returns both named and anonymous images. The default is **`'false'`**. |
| **`limit`** | **`number`** | Sets the page size, clamped to **`1`** through **`200`**. The default is **`100`**. |

- **`page.nextCursor`** is set when more images exist.


```bash
daytona image list
```

| **Flag**      | **Type** | **Description**                                         |
| ------------- | -------- | ------------------------------------------------------- |
| **`--anonymous`** | **`string`** | Selects which images to list. **`true`** lists anonymous images only, and **`any`** lists both named and anonymous images. |
| **`--limit`** | **`int`** | Sets the page size. The default is **`100`**. |


```bash
curl 'https://api.daytona.com/images' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

| **Field**    | **Type** | **Description**                                                                 |
| ------------ | -------- | ------------------------------------------------------------------------------- |
| **`limit`** | **`integer`** | Sets the page size, clamped to **`1`** through **`200`**. The default is **`100`**. |
| **`cursor`** | **`string`** | Resumes the list after the previous page. Pass the **`next_cursor`** of that page. |
| **`name`** | **`string`** | Filters the list by exact image name. |
| **`anonymous`** | **`string`** | Selects which images to list. **`false`** lists named images, **`true`** lists anonymous images, and **`any`** lists both. The default is **`false`**. |
| **`sandbox_id`** | **`string`** | Lists only the captures taken from this sandbox. |


Each image has the following fields:

| **Field**       | **Description**                                                                                                              |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **`id`**            | Image ID (**`img_...`**), used by the get, promote, convert, build-logs-url, and delete endpoints; returned as **`id`** when the image is created |
| **`name`**          | The name sandboxes reference through **`image`**                                                                                 |
| **`status`**        | **`pending`**, **`building`**, **`ready`**, or **`failed`**                                                                                  |
| **`error`**         | Build failure reason; **`null`** unless **`status`** is **`failed`**                                                                     |
| **`source`**        | The build source as submitted: a registry reference, a named image, or a Dockerfile context; **`null`** for captured images     |
| **`region`**        | Home region the image was built or pulled in                                                                                 |
| **`available_in`**  | Regions that hold a copy of the image right now; the home region is listed only while its own copy is there. Empty until the build finishes and after every copy is evicted |
| **`manifest_name`** | Name of the converted image manifest; **`null`** until the build completes                                                       |
| **`bytes`**         | Size of the converted image in bytes; **`null`** until the build completes                                                       |
| **`layer_count`**   | Number of layers in the converted image                                                                                      |
| **`format_version`** | Storage format of the image: **`1`** for images registered by the migration from Daytona v1, **`2`** for the current format         |
| **`format_outdated`** | **`true`** when **`format_version`** is below the current format; [convert](#convert-images) the image before use outside Linux sandboxes |
| **`created_at`**    | Creation timestamp                                                                                                           |
| **`cursor`**        | Token to resume the list immediately after this row; returned by list only                                                   |

#### Iterate over all images

Iterate over every image in the project without handling page cursors. The iterator requests the next page when the current page is exhausted.


```python
from daytona import Daytona

daytona = Daytona()

for image in daytona.v2.images.iter():
    print(image.name, image.status)
```


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

for await (const image of daytona.v2.images.iter()) {
  console.log(image.name, image.status)
}
```


```bash
# Follow every page until the server returns no cursor
daytona image list --all
```


```bash
curl 'https://api.daytona.com/images?cursor={next_cursor}' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

- Pass **`next_cursor`** from the response as the **`cursor`** query parameter to get the next page.


### Get an image

Get an image by its ID. The response carries the image fields described under [list images](#list-images), including its status and failure reason. A name is not accepted; to find an image by name, filter the image list by name.


```python
from daytona import Daytona

daytona = Daytona()

image = daytona.v2.images.get("{imageId}")
print(image.name, image.status)
```

- To find an image by name, use **`images.list(name="NAME")`**.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

const image = await daytona.v2.images.get('{imageId}')
console.log(image.name, image.status)
```

- To find an image by name, use **`images.list({ name: 'NAME' })`**.


```bash
daytona image get {imageId}
```

- To find an image by name, run **`daytona image list --name NAME`**.


```bash
curl 'https://api.daytona.com/images/{imageId}' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

- To find an image by name, use **`GET /images?name=NAME`**.


### Delete images

Delete an image. Deletion fails while a sandbox created from the image is not deleted. A captured image also cannot be deleted while another capture, sandbox, or snapshot still uses its files.


```python
from daytona import Daytona

daytona = Daytona()

daytona.v2.images.delete("{imageId}")
```

- **`images.delete`** takes the image ID, **`img_...`**.


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

await daytona.v2.images.delete('{imageId}')
```

- **`images.delete`** takes the image ID, **`img_...`**.


```bash
daytona image delete {imageId}
```

- The command takes the image ID.
- The command asks for confirmation, and a non-interactive shell requires **`--yes`**.


```bash
curl 'https://api.daytona.com/images/{imageId}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

- The call takes the image ID, not the name.
- The call requires the **`delete:images`** permission.


### Build logs

Read the output of an image build. Following the log streams output while the build runs and ends when it finishes. Stopping the reader does not stop the build.


```python
from daytona import Daytona
from daytona.v2 import ImageBuilder

daytona = Daytona()

image = daytona.v2.images.build(ImageBuilder.from_dockerfile("FROM python:3.12-slim"), name="my-awesome-image")
daytona.v2.images.follow_build_logs(image.id, on_logs=lambda chunk: print(chunk, end=""))
```

| **Parameter** | **Type** | **Description**                                         |
| ------------- | -------- | ------------------------------------------------------- |
| **`timeout`** | **`float`** | Bounds the follow in seconds. When the timeout expires, the call raises **`BuildLogInterruptedError`** instead of returning. The **`cursor`** of the error resumes the follow. By default, there is no deadline. |

The response has the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| **`reason`** | The reason is **`terminal`**, **`deadline`**, **`not_applicable`**, **`resource_deleted`**, or **`unknown`**. |
| **`cursor`** | Resumes the follow after the last output. Pass this value in a later call. |

- **`follow_build_logs`** returns **`status`**, **`reason`**, **`output_complete`**, and **`cursor`**.


```typescript
import { Daytona } from '@daytona/sdk'
import { ImageBuilder } from '@daytona/sdk/v2'

const daytona = new Daytona()

const image = await daytona.v2.images.build(ImageBuilder.fromDockerfile('FROM python:3.12-slim'), { name: 'my-awesome-image' })
await daytona.v2.images.followBuildLogs(image.id, { onLogs: (chunk) => { process.stdout.write(chunk) } })
```

| **Parameter** | **Type** | **Description**                                                                                |
| ------------- | -------- | ---------------------------------------------------------------------------------------------- |
| **`timeout`** | **`number`** | Bounds the follow in milliseconds, unlike the Python SDK, which uses seconds. When the timeout expires, the call throws **`BuildLogInterruptedError`** instead of returning. The **`cursor`** of the error resumes the follow. By default, there is no deadline. |

The response has the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| **`reason`** | The reason is **`terminal`**, **`deadline`**, **`not_applicable`**, **`resource_deleted`**, or **`unknown`**. |
| **`cursor`** | Resumes the follow after the last output. Pass this value in a later call. |

- **`followBuildLogs`** returns **`status`**, **`reason`**, **`outputComplete`**, and **`cursor`**.


```bash
# Stream output until the build finishes
daytona image logs {imageId} --follow
```

| **Flag**    | **Type** | **Description**                                                                                                              |
| ----------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **`--follow`** | **`bool`** | Streams output until the build finishes. The command stops after **`--timeout`** and exits with an error, but the build keeps running. Without **`--follow`**, the command prints the output so far. |
| **`--timeout`** | **`string`** | Sets how long **`--follow`** runs before it stops. The default is **`10m`**. |


```bash
BUILD_LOGS_URL=$(curl 'https://api.daytona.com/images/{imageId}/build-logs-url' \
  --header 'Authorization: Bearer YOUR_API_KEY' | jq -r '.url')

# Stream output until the build finishes
curl --no-buffer "$BUILD_LOGS_URL?follow=true&format=sse" \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: text/event-stream'
```


### Build status

Check an image build's progress. An image moves from pending to building and ends as ready or failed, with a failure reason when it fails.


```python
from daytona import Daytona

daytona = Daytona()

image = daytona.v2.images.get("{imageId}")
print(image.id, image.status, image.error)
```


```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()

const image = await daytona.v2.images.get('{imageId}')
console.log(image.id, image.status, image.error)
```


```bash
# Returns on ready or failed, or after timeout_ms
curl 'https://api.daytona.com/images/{imageId}?wait=ready&timeout_ms=60000' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

The response has the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| **`status`** | The image status is **`pending`**, **`building`**, **`ready`**, or **`failed`**. |
| **`error`** | Carries the failure detail. |