# Images (/docs/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](/docs/snapshots) saves the memory and disk of a running sandbox, and a restored sandbox resumes its processes.

<ImageVisual />

| **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 or Windows sandbox.                            |
| **Windows base image** | A base image Daytona provides for Windows sandboxes. It boots in every region.                      |
| **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 [#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. A repeat build of the same Dockerfile and build context reuses the earlier build instead of running a new one. A Dockerfile that copies local files needs a [build context](#build-context) of at most 1 GiB.

- **Configured sandboxes as templates.** A sandbox set up by hand or by an agent is captured, and new sandboxes start from its filesystem. The source sandbox keeps running during the capture. A named capture stays until you delete it.

- **Private application images.** Images that a CI pipeline pushes to a private registry are imported with the project's [registry credentials](/docs/registries) and boot in sandboxes. Pulling again under the same name re-imports the image, and creates from it fail until the new pull is ready. The API call returns once the pull is queued, and the SDKs and the CLI wait by default until the image is ready.

- **Environments written by agents.** An agent writes a Dockerfile, builds it, and boots sandboxes from the result, without access to your build infrastructure. The SDKs build the image and create the sandbox in one call. A failed build creates no sandbox.

- **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. An image holds no running processes or memory, so each run boots from the same files. Runs that need processes already running start from a [snapshot](/docs/snapshots) instead, which saves memory and disk.

## Create sandboxes from images [#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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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")
```

<Callout type="note" title="Notes">
- The requests require the `write:images` and `write:sandboxes` permissions.
- Set `timeout` in seconds to wait in one call until the image is ready or failed. When the time runs out first, the call raises `RequestTimeoutError`.
- A create rejects a value that contains `/` or `:` as a registry reference, so pass the name or ID of a registered image.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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')
```

<Callout type="note" title="Notes">
- The requests require the `write:images` and `write:sandboxes` permissions.
- Set `timeoutSeconds` to wait in one call until the image is ready or failed. When the time runs out first, the call throws `RequestTimeoutError`.
- A create rejects a value that contains `/` or `:` as a registry reference, so pass the name or ID of a registered image.
</Callout>

</TabItem>
<TabItem label="CLI" icon="seti:shell">

```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
```

<Callout type="note" title="Notes">
- The requests require the `write:images` and `write:sandboxes` permissions.
- A create rejects a value that contains `/` or `:` as a registry reference, so pass the name or ID of a registered image.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```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" }'
```

<Callout type="note" title="Notes">
- The requests require the `write:images` and `write:sandboxes` permissions.
- The default `timeout_ms` is `30000`, and the maximum is `60000`. Larger values are treated as `60000`.
- A create rejects a value that contains `/` or `:` as a registry reference, so pass the name or ID of a registered image.
</Callout>

</TabItem>
</Tabs>

### Inline Dockerfile [#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. If the same Dockerfile was built before under a name, the sandbox starts from that named image instead. 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 window starts when the image is created, so a window shorter than the build deletes the image before it is ready.

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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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**                                                                                                                                         |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `build_timeout` | float | Sets how many seconds the build can take. A build that runs longer fails with `408` `REQUEST_TIMEOUT` and creates no sandbox. The build keeps running, and its anonymous image is deleted after its idle window. By default, or with `0`, there is no limit. |
| `wait_seconds` | int | Sets how many seconds the create waits for the sandbox to start, as in `create`. It does not limit the build. The default is `60`. |
| `idle_expiry_seconds` | int | Sets the idle window of the image in seconds. It applies only to the image. It is ignored when the build reuses the build of a named image. |
| `region` | str | Selects the region for both the build and the sandbox. |

<Callout type="note" title="Notes">
- The requests require the `write:images` and `write:sandboxes` permissions.
- `cpu_arch` also applies to both the build and the sandbox.
- Every other keyword argument goes to `create`.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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**                                                                                                                                         |
| ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `buildTimeoutSeconds` | number | Sets how many seconds the build can take. A build that runs longer fails with `408` `REQUEST_TIMEOUT` and creates no sandbox. The build keeps running, and its anonymous image is deleted after its idle window. By default, or with `0`, there is no limit. |
| `waitSeconds` | number | Sets how many seconds the create waits for the sandbox to start, as in `create`. It does not limit the build. The default is `60`. |
| `idleExpirySeconds` | number | Sets the idle window of the image in seconds. It applies only to the image. It is ignored when the build reuses the build of a named image. |
| `region` | string | Selects the region for both the build and the sandbox. |

<Callout type="note" title="Notes">
- The requests require the `write:images` and `write:sandboxes` permissions.
- `cpuArch` also applies to both the build and the sandbox.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```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 is required. This field cannot be sent together with `source`. |
| `name` | string | Sets the image name. Without a name, the image is anonymous, unless the build reuses the build of a named image; the call then returns that named image. |
| `idle_expiry_seconds` | integer | Sets the idle window of the image in seconds, from `1` up to the server maximum. `-1` sets the maximum. Other values return `400` `IDLE_OUT_OF_RANGE`. The default is the server maximum. It is ignored when the build reuses the build of a named image. When the build reuses an anonymous image, that image keeps the longer of the two windows. |

</TabItem>
</Tabs>

### Windows images [#windows-images]

Create a [Windows sandbox](/docs/windows-sandboxes) from a Windows image. Windows sandboxes boot only from Windows images: a platform base image, which boots in every region, or a Windows image captured in the project, which boots only in the region it lives in.

<Tabs syncKey="language">
<TabItem label="API" icon="seti:json">

{/* snippet-check: skip POST /sandboxes/windows is hidden from the public API contract until relaunch. */}
```bash
curl 'https://api.daytona.com/sandboxes/windows' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "image": "win2022" }'
```

| **Field** | **Type** | **Description**                                                                                             |
| --------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `image` | string | Sets the Windows image by name or ID. The image is a platform base image or a Windows image captured in the project. This field is required. |

<Callout type="note" title="Notes">
- The request requires the `write:sandboxes` permission.
- The organization must have the `windows_sandboxes` feature flag.
- Windows sandboxes are available on request.
</Callout>

</TabItem>
</Tabs>

## Pull images [#pull-images]

Pull an image from a registry and register it under a name. The pull runs in the background. The API call returns once the pull is queued, and the SDKs and the CLI wait until the image is ready by default. Pulling again under the name of an existing registry or Dockerfile image re-imports the image: it keeps its ID, returns to pending, and sandbox creates from it fail until the new pull is ready. A failed pull keeps its name, so pull again under the name to retry. A private registry authenticates with the [registry credential](/docs/registries) stored for the image host.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

daytona = Daytona()

image = daytona.v2.images.pull("node:22-slim", 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 `.`, `sbx_`, `vol_`, `fn_`, `snap-`, `build-`, `refs`, `by-digest`, `prj-`, or `glb-`. It must not have the format of an image ID, `img_...`. 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. Images and snapshots share one name space per project. A name that belongs to a captured image, a Windows image, or a snapshot returns `409`. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- `images.pull` waits until the image is ready unless you pass `wait=False`.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

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

const daytona = new Daytona()

const image = await daytona.v2.images.pull('node:22-slim', { 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 `.`, `sbx_`, `vol_`, `fn_`, `snap-`, `build-`, `refs`, `by-digest`, `prj-`, or `glb-`. It must not have the format of an image ID, `img_...`. 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. Images and snapshots share one name space per project. A name that belongs to a captured image, a Windows image, or a snapshot returns `409`. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- `images.pull` waits until the image is ready unless you pass `wait: false`.
</Callout>

</TabItem>
<TabItem label="CLI" icon="seti:shell">

```bash
daytona image create my-awesome-image --ref node:22-slim
```

| **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. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- The name is 1 to 128 characters of letters, digits, `.`, `_`, and `-`.
- The name must not start with `.`, `sbx_`, `vol_`, `fn_`, `snap-`, `build-`, `refs`, `by-digest`, `prj-`, or `glb-`.
- The name must not have the format of an image ID, `img_...`.
- Images and snapshots share one name space per project, so the name must not belong to a captured image, a Windows image, or a snapshot.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```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": "node:22-slim" }
}'
```

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- Wait with `GET /images/{imageId}?wait=ready`, using the `id` from the response, before creating sandboxes.
- The name is 1 to 128 characters of letters, digits, `.`, `_`, and `-`.
- The name must not start with `.`, `sbx_`, `vol_`, `fn_`, `snap-`, `build-`, `refs`, `by-digest`, `prj-`, or `glb-`.
- The name must not have the format of an image ID, `img_...`.
- Images and snapshots share one name space per project, so the name must not belong to a captured image, a Windows image, or a snapshot.
</Callout>

</TabItem>
</Tabs>

## Build images [#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. A build without a name that reuses the build of a named image returns that named image, which stays permanent.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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, unless the build reuses the build of a named image; the call then returns that named image. |
| `idle_expiry_seconds` | int | Sets how many seconds an anonymous image can go without a sandbox create before deletion. `-1` sets the maximum. The default is the server maximum. It is allowed only without `name`. It is ignored when the build reuses the build of a named image. When the build reuses an anonymous image, that image keeps the longer of the two windows. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- `images.build` takes an `ImageBuilder`, not a raw Dockerfile string.
- Wait with `images.get(image.id, wait="ready")` before you use the image.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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, unless the build reuses the build of a named image; the call then returns that named image. |
| `idleExpirySeconds` | number | Sets how many seconds an anonymous image can go without a sandbox create before deletion. `-1` sets the maximum. The default is the server maximum. It is allowed only without `name`. It is ignored when the build reuses the build of a named image. When the build reuses an anonymous image, that image keeps the longer of the two windows. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- Wait with `images.get(image.id, { wait: 'ready' })` before you use the image.
</Callout>

</TabItem>
<TabItem label="CLI" icon="seti:shell">

```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
```

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- The command waits up to `--timeout`, which defaults to `10m`.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```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, unless the build reuses the build of a named image; the call then returns that named image. |

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. |

</TabItem>
</Tabs>

### Build context [#build-context]

Build an image from a Dockerfile and a build context. Daytona uploads the context to the image's home region and runs the build there. 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). The SDKs and the CLI upload a context of up to 1 GiB.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

daytona = Daytona()

# The context holds the Dockerfile at its root
image = daytona.v2.images.build_from_context("context.tar", name="my-awesome-image")
```

| **Parameter** | **Type** | **Description** |
| ------------- | -------- | --------------- |
| `context` | bytes or str | Sets the build context: the bytes of a tar archive, or the path of a `.tar` file. The archive is at most 1 GiB. This parameter is required. |
| `dockerfile` | str | Sets the path of the Dockerfile inside the context. The default is `Dockerfile`. |
| `name` | str | Sets the image name. Without a name, the image is anonymous, unless the build reuses the build of a named image; the call then returns that named image. |
| `idle_expiry_seconds` | int | Sets how many seconds an anonymous image can go without a sandbox create before deletion. The default is the server maximum. It is allowed only without `name`. It is ignored when the build reuses the build of a named image. When the build reuses an anonymous image, that image keeps the longer of the two windows. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- `region` and `cpu_arch` work as in [build options](#build-options).
- Wait with `images.get(image.id, wait="ready")` before you use the image.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

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

const daytona = new Daytona()

// The context holds the Dockerfile at its root
const image = await daytona.v2.images.buildFromContext('context.tar', { name: 'my-awesome-image' })
```

| **Parameter** | **Type** | **Description** |
| ------------- | -------- | --------------- |
| `context` | Uint8Array, Blob, or string | Sets the build context: the bytes of a tar archive, or in Node.js the path of a `.tar` file. The archive is at most 1 GiB. This parameter is required. |
| `dockerfile` | string | Sets the path of the Dockerfile inside the context. The default is `Dockerfile`. |
| `name` | string | Sets the image name. Without a name, the image is anonymous, unless the build reuses the build of a named image; the call then returns that named image. |
| `idleExpirySeconds` | number | Sets how many seconds an anonymous image can go without a sandbox create before deletion. The default is the server maximum. It is allowed only without `name`. It is ignored when the build reuses the build of a named image. When the build reuses an anonymous image, that image keeps the longer of the two windows. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- `region` and `cpuArch` work as in [build options](#build-options).
- Wait with `images.get(image.id, { wait: 'ready' })` before you use the image.
</Callout>

</TabItem>
<TabItem label="CLI" icon="seti:shell">

```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. A directory is uploaded without the files its `.dockerignore` excludes. The uploaded context is at most 1 GiB. 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. |

</TabItem>
<TabItem label="API" icon="seti:json">

```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, unless the build reuses the build of a named image; the call then returns that named image. |
| `dockerfile` | string | Sets the path of the Dockerfile inside the context. The default is `Dockerfile`. |
| `region_id` | 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. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- The multipart form takes a context of at most 64 MiB. Upload a larger context, up to 1 GiB, as in [large build contexts](#large-build-contexts).
- Wait with `GET /images/{imageId}?wait=ready` before creating sandboxes.
</Callout>

</TabItem>
</Tabs>

### Large build contexts [#large-build-contexts]

Upload a build context of up to 1 GiB and build an image from it. The upload declares the archive by its size and SHA-256, sends the archive in parts straight to storage, and is then completed. Completing the upload verifies the stored archive: its size and SHA-256 must match the declared ones, it must be a well-formed tar archive, and its Dockerfile must be accepted for a build. An archive that fails verification is deleted.

An upload belongs to one region, and only a build in that region can use it. When the project already holds a verified upload of the same archive in the region, Daytona returns that upload and the archive is not sent again. The SDKs and the CLI run these steps when they upload a [build context](#build-context).

<Tabs syncKey="language">
<TabItem label="API" icon="seti:json">

```bash
# Size in bytes and SHA-256 of the archive (on macOS use: shasum -a 256 context.tar)
wc -c < context.tar
sha256sum context.tar

# Declare the upload: replace 10240 with the size from wc and {sha256} with the hash from sha256sum
curl 'https://api.daytona.com/build-contexts' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "bytes": 10240,
  "sha256": "{sha256}"
}'

# Split the archive into parts of part_size bytes: part 1 is part-aa, part 2 is part-ab, and so on
split -b {partSize} context.tar part-

# Upload each part to its url, without an Authorization header
curl '{partUrl}' --upload-file part-aa

# Verify the uploaded archive
curl 'https://api.daytona.com/build-contexts/{uploadId}/complete' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "region_id": "{regionId}"
}'

# Build from the verified upload
curl 'https://api.daytona.com/images' \
  --request POST \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-awesome-image",
  "context_upload_id": "{uploadId}"
}'
```

| **Field** | **Type** | **Description** |
| --------- | -------- | --------------- |
| `bytes` | integer | Sets the size of the archive in bytes, from `1` to `1073741824` (1 GiB). This field is required. |
| `sha256` | string | Sets the SHA-256 of the archive, as 64 lowercase hex characters. This field is required. |
| `dockerfile` | string | Sets the path of the Dockerfile inside the context. The default is `Dockerfile`. |
| `region_id` | string | In the declare request, sets the region of the upload. The default is the resolved default region. In the complete request, it is the `region_id` the declare request returned. This field is required in the complete request. |
| `context_upload_id` | string | Sets the `ready` upload to build. The image must be created in the region of the upload. It cannot be combined with `dockerfile` or `source`. |
| `name` | string | Sets the image name. Without a name, the image is anonymous, unless the build reuses the build of a named image; the call then returns that named image. |

The declare and complete requests return the upload, with the following fields:

| **Field** | **Description** |
| --------- | --------------- |
| `id` | Identifies the upload. |
| `region_id` | The region the upload belongs to. |
| `state` | The state is `uploading` until the upload is completed and verified, then `ready`. |
| `part_size` | The size of every part but the last, in bytes. It is set while the upload is `uploading`. |
| `parts` | The parts to upload, each with its `part_number`, its size in `bytes`, and its `url`. It is empty once the upload is `ready`. |
| `urls_expire_at` | The time the part URLs stop accepting uploads. |
| `upload_expires_at` | The time an upload that was not completed expires and is deleted. |
| `expires_at` | The time a `ready` upload can no longer be used by a build. |

<Callout type="note" title="Notes">
- The requests require the `write:images` permission.
- When the declare request returns the upload as `ready`, the archive is already stored; skip to the build.
- Part `n` holds the archive bytes from `(n - 1) * part_size` on. Each part URL accepts one `PUT` of exactly the part's `bytes`. The URL is a credential; do not log or share it.
- Upload every part before `urls_expire_at`, complete the upload before `upload_expires_at`, and build before `expires_at`.
- Wait with `GET /images/{imageId}?wait=ready` before creating sandboxes.
</Callout>

</TabItem>
</Tabs>

### Build options [#build-options]

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

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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-east",
    cpu_arch="amd64",
)
```

| **Parameter** | **Type** | **Description**                                                                                                      |
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `region` | str | Sets the home region of the image. Earth and country-wide regions such as `us` return an error. 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`. A build for an architecture that no runner in the region provides stays `pending`. `images.pull` also accepts `cpu_arch`, but a pull ignores the value and uses the region's architecture. A `from_registry` builder with no steps ignores the value the same way. The default is the region's architecture. |

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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-east', cpuArch: 'amd64' },
)
```

| **Parameter** | **Type**  | **Description**                                                                                            |
| ------------- | --------- | ---------------------------------------------------------------------------------------------------------- |
| `region` | string | Sets the home region of the image. Earth and country-wide regions such as `us` return an error. 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`. A build for an architecture that no runner in the region provides stays `pending`. `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. |

</TabItem>
<TabItem label="CLI" icon="seti:shell">

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

| **Flag**   | **Type** | **Description**                                                                                                         |
| ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
| `--region` | string | Sets the home region of the image. Earth and country-wide regions such as `us` return an error. 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`. A build for an architecture that no runner in the region provides stays `pending`. Without `cpu_arch`, the image is built for the region's architecture. |

</TabItem>
<TabItem label="API" icon="seti:json">

```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_id": "us-east",
  "cpu_arch": "amd64"
}'
```

| **Field**  | **Type** | **Description**                                                                                            |
| ---------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `region_id` | string | Sets the home region of the image. Earth and country-wide regions such as `us` return an error. 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`. A build for an architecture that no runner in the region provides stays `pending`. The default is the region's architecture. |

</TabItem>
</Tabs>

### Declarative builds [#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. A sandbox runs as the image's user, or as root when the image sets none, sets root, or sets a numeric user ID. A user set when the sandbox is created replaces the image's user.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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`. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- Each method returns a new builder and leaves the original unchanged, so one base builder can start several images.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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 and for sandboxes from the image, rendered as `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. |

<Callout type="note" title="Notes">
- The request requires the `write:images` permission.
- Each method returns a new builder and leaves the original unchanged, so one base builder can start several images.
</Callout>

</TabItem>
</Tabs>

### Preview the Dockerfile [#preview-the-dockerfile]

Render the Dockerfile a builder describes, without submitting a build. Use it to review the generated instructions, or to send the build yourself through the API as the `dockerfile` field of `POST /images`.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona.v2 import ImageBuilder

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

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

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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
```

</TabItem>
</Tabs>

## Capture images [#capture-images]

Capture the filesystem of a sandbox as an image, and boot new sandboxes from the image. The capture runs in the background, and the image becomes ready when the capture finishes. Linux and Windows sandboxes are captured while running, and they keep running. A macOS sandbox is captured when it is persistent and stopped, as a [filesystem snapshot](/docs/macos-sandboxes#filesystem-snapshot). A GPU sandbox saves its state as a [checkpoint](/docs/snapshots) instead.

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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

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

image = sandbox.capture_image(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. The default is `None`, which makes the image anonymous. The name must be unique in the project. |
| `idle_expiry_seconds` | int | Sets how many seconds an anonymous image can go without a sandbox create before deletion. `-1` sets the maximum. The default is the server maximum. It is allowed only without `name`. |

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

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

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

let image = await sandbox.captureImage({ 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. The default is `undefined`, which makes the image anonymous. The name must be unique in the project. |
| `idleExpirySeconds` | number | Sets how many seconds an anonymous image can go without a sandbox create before deletion. `-1` sets the maximum. The default is the server maximum. It is allowed only without `name`. |

</TabItem>
<TabItem label="CLI" icon="seti:shell">

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

# Anonymous capture, deleted after the server maximum idle window without a sandbox create
daytona image capture {sandboxId}
```

| **Flag**   | **Type** | **Description**                                                                                                             |
| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `--name` | string | Sets the image name, which must be unique in the project. Without a name, the image is anonymous. |
| `--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. |

</TabItem>
<TabItem label="API" icon="seti:json">

```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. Without a name, the image is anonymous. The name must be unique in the project. |
| `idle_expiry_seconds` | integer | Sets how many seconds an anonymous image can go without a sandbox create before deletion. `-1` sets the maximum. The default is the server maximum. It is allowed only without `name`. |

<Callout type="note" title="Notes">
- The request requires the `write:images` and `write:sandboxes` permissions.
- Wait with `GET /images/{imageId}?wait=ready` before using the image.
</Callout>

</TabItem>
</Tabs>

## Promote images [#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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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. This parameter is required. The name must be unique in the project. |

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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. This parameter is required. The name must be unique in the project. |

</TabItem>
<TabItem label="CLI" icon="seti:shell">

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

</TabItem>
<TabItem label="API" icon="seti:json">

```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. This field is required. The name must be unique in the project. |

</TabItem>
</Tabs>

## Convert images [#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. GPU sandboxes, Windows sandboxes, and 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.

<Tabs syncKey="language">
<TabItem label="API" icon="seti:json">

```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** |
| --------- | --------------- |
| `id` | Identifies the image. |
| `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. |
| `from_format` | The `format_version` of the image before the call. |
| `to_format` | The format the conversion targets, the current one. |
| `status` | The image status after the call: `pending` or `building` while the image converts, or its own status when it is already in the current format. |

<Callout type="note" title="Notes">
- The requests require the `write:images` permission.
- The call takes the image ID, `img_...`, or the image name.
- An image that needs conversion has `format_outdated` set to `true`.
</Callout>

</TabItem>
</Tabs>

## Operations [#operations]

### List images [#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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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 | Selects which images to list. `True` returns anonymous images only, and `False` returns named images only. The default is `False`. |
| `limit` | int | Sets the page size, from `1` to `200`. The default is `100`. |
| `cursor` | str | Resumes the list after the previous page. Pass the `next_cursor` of that page, which is set when more images exist. |
| `name` | str | Filters the list by exact image name. |
| `sandbox_id` | str | Lists only the captures taken from this sandbox. |
| `platform` | str | Selects platform images, which Daytona publishes for every project. `"true"` lists platform images only, `"false"` lists the project's images only, and `"any"` lists both. The default is `"false"`. |

<Callout type="note" title="Notes">
- The request requires the `read:images` permission.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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` | boolean | Selects which images to list. `true` returns anonymous images only, and `false` returns named images only. The default is `false`. |
| `limit` | number | Sets the page size, from `1` to `200`. The default is `100`. |
| `cursor` | string | Resumes the list after the previous page. Pass the `nextCursor` of that page, which is set when more images exist. |
| `name` | string | Filters the list by exact image name. |
| `sandboxId` | string | Lists only the captures taken from this sandbox. |
| `platform` | string | Selects platform images, which Daytona publishes for every project. `'true'` lists platform images only, `'false'` lists the project's images only, and `'any'` lists both. The default is `'false'`. |

<Callout type="note" title="Notes">
- The request requires the `read:images` permission.
</Callout>

</TabItem>
<TabItem label="CLI" icon="seti:shell">

```bash
daytona image list
```

| **Flag**      | **Type** | **Description**                                         |
| ------------- | -------- | ------------------------------------------------------- |
| `--anonymous` | string | Selects which images to list. `true` lists anonymous images only, and `false` lists named images only. The default is `false`. |
| `--platform` | string | Selects platform images, which Daytona publishes for every project. `true` lists platform images only, `false` lists the project's images only, and `any` lists both. The default is `false`. |
| `--limit` | int | Sets the page size, from `1` to `200`. The default is `100`. |
| `--cursor` | string | Resumes the list after the previous page. Pass the `next_cursor` of that page. |
| `--all` | bool | Follows every page until the list is exhausted. |
| `--name` | string | Filters the list by exact image name. |
| `--sandbox` | string | Lists only the captures taken from this sandbox. |
| `--region` | string | Lists only the images available in this region or a region inside it. A page can then hold fewer than `--limit` images. By default, the list covers every region. |

<Callout type="note" title="Notes">
- The request requires the `read:images` permission.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```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` | boolean | Selects which images to list. `false` lists named images, and `true` lists anonymous images. The default is `false`. |
| `sandbox_id` | string | Lists only the captures taken from this sandbox. |
| `platform` | string | Selects platform images, which Daytona publishes for every project. `true` lists platform images only, `false` lists the project's images only, and `any` lists both. The default is `false`. |

<Callout type="note" title="Notes">
- The request requires the `read:images` permission.
</Callout>

</TabItem>
</Tabs>

Each image has the following fields. The SDKs return `region_id` as `region`, `region_ids` as `available_in` (`availableIn` in TypeScript), and `class` as `sandbox_class` (`sandboxClass` in TypeScript).

| **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                                                                         |
| `origin`          | How the image was created, such as `registry`, `dockerfile`, or `capture`                                                                                                                       |
| `class`           | Sandbox class the image supports, such as `linux` or `macos`                                                                                                                                    |
| `anonymous`       | `true` when the image has no name                                                                                                                                                               |
| `retention`       | `permanent` for a named image, `idle` for an anonymous image                                                                                                                                    |
| `expires_at`      | When an anonymous image is deleted unless a sandbox create restarts its idle window; `null` for a permanent image                                                                              |
| `region_id`       | Home region the image was built, pulled, or captured in                                                                                                                                         |
| `region_ids`      | 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                     |
| `platform`        | `true` for a platform image that Daytona publishes for every project                                                                                                                            |
| `default`         | `true` for the image that a sandbox create without an image boots                                                                                                                               |
| `deprecated`      | `true` for a deprecated platform image, which is not listed and cannot be used by name; its ID still resolves                                                                                   |
| `bytes`           | Size of the converted image in bytes; `null` until the build completes                                                                                                                          |
| `layer_count`     | Number of layers in the converted image                                                                                                                                                         |
| `disk_mib`        | Disk size in MiB of a macOS sandbox created from the image without `disk_mib`, and the smallest disk such a create accepts; set for images captured from a macOS sandbox, `null` otherwise      |
| `format_version`  | Storage format of the image: `1` for images registered by the migration from Daytona v0, `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                                                               |
| `imported_from`   | Source platform and resource ID of an image imported by the [migration from Daytona v0](/docs/migrate-from-v0), as `{"platform": "v0", "id": "..."}`; absent for an image created on Daytona v2 |
| `created_at`      | Creation timestamp                                                                                                                                                                              |
| `updated_at`      | Timestamp of the last change                                                                                                                                                                    |
| `cursor`          | Token to resume the list immediately after this row; returned by list only                                                                                                                      |

#### Iterate over all images [#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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

daytona = Daytona()

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

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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)
}
```

</TabItem>
<TabItem label="CLI" icon="seti:shell">

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

</TabItem>
<TabItem label="API" icon="seti:json">

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

</TabItem>
</Tabs>

### Get an image [#get-an-image]

Get an image by its ID or name. The response carries the image fields described under [list images](#list-images), including its status and failure reason.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

daytona = Daytona()

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

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

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

const daytona = new Daytona()

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

</TabItem>
<TabItem label="CLI" icon="seti:shell">

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

</TabItem>
<TabItem label="API" icon="seti:json">

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

</TabItem>
</Tabs>

### Delete images [#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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

daytona = Daytona()

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

<Callout type="note" title="Notes">
- The request requires the `delete:images` permission.
- `images.delete` takes the image ID, `img_...`, or the image name.
</Callout>

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

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

const daytona = new Daytona()

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

<Callout type="note" title="Notes">
- The request requires the `delete:images` permission.
- `images.delete` takes the image ID, `img_...`, or the image name.
</Callout>

</TabItem>
<TabItem label="CLI" icon="seti:shell">

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

<Callout type="note" title="Notes">
- The request requires the `delete:images` permission.
- The command takes the image ID or the name.
- The command asks for confirmation, and a non-interactive shell requires `--yes`.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

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

<Callout type="note" title="Notes">
- The request requires the `delete:images` permission.
- The call takes the image ID or the name.
</Callout>

</TabItem>
</Tabs>

### Build logs [#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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```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",
)
daytona.v2.images.follow_build_logs(image.id, on_logs=lambda chunk: print(chunk, end=""))
```

| **Parameter** | **Type** | **Description**                                         |
| ------------- | -------- | ------------------------------------------------------- |
| `on_logs` | Callable | Receives the build output as text. Without `on_event`, it also receives one line per state change and per `gap`, `unavailable`, `source_changed`, or `error` event, prefixed with the event type, such as `[build logs: state]`. This parameter is required. |
| `on_event` | Callable | Receives every stream event, including state changes. With `on_event`, these events no longer reach `on_logs`. |
| `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** |
| --------- | --------------- |
| `status` | The image status when the follow ended. |
| `reason` | The reason is `terminal`, `deadline`, `not_applicable`, `resource_deleted`, or `unknown`. |
| `output_complete` | The value is `true` when all build output was delivered. |
| `cursor` | Resumes the follow after the last output. Pass this value in a later call. |

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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' },
)
await daytona.v2.images.followBuildLogs(image.id, { onLogs: (chunk) => { process.stdout.write(chunk) } })
```

| **Parameter** | **Type** | **Description**                                                                                |
| ------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `onLogs` | function | Receives the build output as text. Without `onEvent`, it also receives one line per state change and per `gap`, `unavailable`, `source_changed`, or `error` event, prefixed with the event type, such as `[build logs: state]`. This parameter is required. |
| `onEvent` | function | Receives every stream event, including state changes. With `onEvent`, these events no longer reach `onLogs`. |
| `timeoutSeconds` | number | Bounds the follow in 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** |
| --------- | --------------- |
| `status` | The image status when the follow ended. |
| `reason` | The reason is `terminal`, `deadline`, `not_applicable`, `resource_deleted`, or `unknown`. |
| `outputComplete` | The value is `true` when all build output was delivered. |
| `cursor` | Resumes the follow after the last output. Pass this value in a later call. |

</TabItem>
<TabItem label="CLI" icon="seti:shell">

```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 log of a finished build, or the build state while the build runs. |
| `--timeout` | string | Sets how long `--follow` runs before it stops. The default is `10m`. |

</TabItem>
<TabItem label="API" icon="seti:json">

```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'
```

The stream has the following events:

| **Event** | **Description** |
| --------- | --------------- |
| `state` | Carries the build `status`, the failure `error`, and `deadline_at`, the time by which the build must finish. |
| `log` | Carries build output in `data_base64` as base64-encoded bytes, with the `start_offset` and `next_offset` of the bytes. |
| `gap` | Reports output that is no longer stored, with the `requested_offset`, the `first_available_offset`, the `dropped_bytes`, and the `reason`. |
| `unavailable` | Reports that the build output cannot be read right now, with the `reason`. The stream keeps retrying the source every 2 seconds. When the build has ended and the output stays unreadable, the stream ends with `done`, where `output_complete` is `false`. |
| `source_changed` | Ends the stream when the log source changed, with the `reason`. |
| `error` | Ends the stream with the `error` detail, its `code`, and whether the request is `retryable`. |
| `done` | Ends the stream with the final `status`, the `reason`, `output_complete`, and the `cursor` that resumes the stream. |

The stream sends the comment line `: heartbeat` every 10 seconds to keep the connection open. It is not an event.

</TabItem>
</Tabs>

### Build status [#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.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

daytona = Daytona()

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

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```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)
```

</TabItem>
<TabItem label="API" icon="seti:json">

```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. |

</TabItem>
</Tabs>

## Permissions [#permissions]

Images are scoped to a project. Access is governed by three permissions, which you can grant to an [organization role](/docs/organizations#roles) or an [API key](/docs/api-keys).

Creating a sandbox from an image requires the permission to create sandboxes, and capturing an image of a sandbox requires the `write:sandboxes` permission as well as `write:images`. The [audit logs](/docs/audit-logs) record image builds, captures, promotions, conversions, and deletes.

| **Permission**      | **Grants**                                        |
| ------------------- | ------------------------------------------------- |
| **`read:images`**   | List and get images, and read their build logs    |
| **`write:images`**  | Build, pull, capture, promote, and convert images |
| **`delete:images`** | Delete images                                     |
