# Images

Unikraft Cloud uses a **registry** to store images used to instantiate apps.
At a high level, you use the CLI to build and push an image to the registry, and then ask the controller to start an app from it.

1. **build**: `kraft` builds the app locally according to a `Dockerfile`.
1. **pkg**: `kraft` packages the app locally into an OCI image.
1. **push**: `kraft` pushes the OCI image to the Unikraft Cloud registry.
1. **create**: The controller instantiates your app from the image according to a `Kraftfile`.

The simplest way to run this workflow is to use a single deploy or run command, which combines all steps into one flow.
Internally, the command calls other subcommands.
The service in the diagram is the mechanism to connect apps to the Internet.
Read more in the [services guide](/platform/services).


## `Dockerfiles`, `Kraftfiles` and runtimes

On Unikraft Cloud, a `Dockerfile` guides the process of building images, and a `Kraftfile` guides the process of deploying the resulting image.

Use the Python running example:

```bash title=""
git clone https://github.com/unikraft-cloud/examples
cd examples/http-python3.12
```

The directory contains the following `Kraftfile`:

```yaml title="Kraftfile"
spec: v0.6

runtime: python:3.12

rootfs: ./Dockerfile

cmd: ["/usr/bin/python3", "/src/server.py"]
```

The file is simple: it defines the start `cmd`, instructs `kraft` to build the root filesystem with a `Dockerfile`, and specifies the `python3.12` **runtime**.
On Unikraft Cloud, a runtime is a base image that contains the (minimal) code needed for the app (in this case the Python interpreter) to run.
Unikraft Cloud then overlays your app code on top of it during the packaging step.

The `Dockerfile` itself for the app looks as follows:

```dockerfile title="Dockerfile"
FROM scratch

# Python HTTP server
COPY ./server.py /src/server.py
```

If you're familiar with `Dockerfiles` there is nothing unusual here, other than that by default Unikraft Cloud uses `FROM scratch` to keep images lean.
To add your app's code to the build, change the `COPY` commands as needed.

All [guides](/guides/bun) on Unikraft Cloud, and the [examples](https://github.com/unikraft-cloud/examples) they rely on underneath come with `Kraftfile`s and `Dockerfile`s for you to get started.

:::tip
You can also try a standard base image (for example, `FROM python:alpine`).
This choice may increase image size, memory use, and boot time.
:::


## Example workflows

This guide uses a Python [app](/guides/python) as an example to show three workflows:

1. How to create an image and launch an instance from it.
2. How to create an image and launch many instances from it.
3. How to launch instances from an existing image.


### Create an image and an instance from it

Start with the simplest workflow: create an image from a Python app (following the Python app [guide](/guides/python)) and start an instance from it:

<CodeTabs syncKey="cli-tool">

```bash title="unikraft"
git clone https://github.com/unikraft-cloud/examples
cd examples/http-python3.12
unikraft build . --output <my-org>/http-python312:latest
unikraft run --metro=fra -p 443:8080/http+tls -m 512MiB --image=<my-org>/http-python312:latest
```

```bash title="kraft"
git clone https://github.com/unikraft-cloud/examples
cd examples/http-python3.12
kraft cloud deploy -p 443:8080 -M 512 .
```

</CodeTabs>

The output should look like:

```ansi title=""
[90m[[0m[92m●[0m[90m][0m Deployed successfully!
 [90m│[0m
 [90m├[0m[90m──────────[0m [90mname[0m: http-python312-ma2i9
 [90m├[0m[90m──────────[0m [90muuid[0m: e7389eee-9808-4152-b2ec-1f3c0541fd05
 [90m├[0m[90m─────────[0m [90mstate[0m: [92mrunning[0m
 [90m├[0m[90m───────────[0m [90murl[0m: https://young-night-5fpf0jj8.fra.unikraft.app
 [90m├[0m[90m─────────[0m [90mimage[0m: <my-org>/http-python312@sha256:278cb8b14f9faf9c2702dddd8bfb6124912d82c11b4a2c6590b6e32fc4049472
 [90m├[0m[90m─────[0m [90mboot time[0m: 15.09 ms
 [90m├[0m[90m────────[0m [90mmemory[0m: 512 MiB
 [90m├[0m[90m───────[0m [90mservice[0m: young-night-5fpf0jj8
 [90m├[0m[90m──[0m [90mprivate fqdn[0m: http-python312-ma2i9.internal
 [90m├[0m[90m────[0m [90mprivate ip[0m: 172.16.3.3
 [90m└[0m[90m──────────[0m [90margs[0m: /usr/bin/python /src/server.py
```

This command builds an image named `http-python312@sha256:278cb8b1...` using the `Kraftfile` and `Dockerfile`.
It then packages it, pushes it to the registry, and starts an instance named `http-python312-ma2i9` from it.
The controller fetches the image from the registry to start the instance.

You can see your images by running the following command:

<CodeTabs syncKey="cli-tool">

```bash title="unikraft"
unikraft images list
```

```bash title="kraft"
kraft cloud image ls
```

</CodeTabs>

You should see output like:

```text title=""
IMAGE                       TAG     SIZE
<my-org>/http-python312  latest  77 MB
```

And you can remove an image from the registry via the legacy CLI:

<CodeTabs>

```bash title="kraft"
kraft cloud img rm http-python312
```

</CodeTabs>

:::note
There may be a delay of a few minutes between removing an image from the registry and the image list reflecting the change.
:::


### Create an image and many instances from it

For the next workflow, start many instances:

<CodeTabs syncKey="cli-tool">

```bash title="unikraft"
git clone https://github.com/unikraft-cloud/examples
cd examples/http-python3.12
unikraft build . --output <my-org>/http-python312:latest
unikraft run --metro=fra -p 443:8080/http+tls -m 512MiB --replicas 2 --image=<my-org>/http-python312:latest
```

```bash title="kraft"
git clone https://github.com/unikraft-cloud/examples
cd examples/http-python3.12
kraft cloud deploy -p 443:8080 -M 512 -R 2 .
```

</CodeTabs>

```ansi title=""
[90m[[0m[92m●[0m[90m][0m Deployed successfully!
 [90m│[0m
 [90m├[0m[90m──────────[0m [90mname[0m: http-python312-8mxq5
 [90m├[0m[90m──────────[0m [90muuid[0m: 37f5b23c-0996-45fa-8d7f-e6b2942eb6fb
 [90m├[0m[90m─────────[0m [90mstate[0m: [92mrunning[0m
 [90m├[0m[90m───────────[0m [90murl[0m: https://small-darkness-4t9y8n5s.fra.unikraft.app
 [90m├[0m[90m─────────[0m [90mimage[0m: http-python312@sha256:5b922dfa1632af38c476b98fdd9f4314fb9c5e587d3d31255e6479108c057e88
 [90m├[0m[90m─────[0m [90mboot time[0m: 153.65 ms
 [90m├[0m[90m────────[0m [90mmemory[0m: 512 MiB
 [90m├[0m[90m───────[0m [90mservice[0m: small-darkness-4t9y8n5s
 [90m├[0m[90m──[0m [90mprivate fqdn[0m: http-python312-8mxq5.internal
 [90m├[0m[90m────[0m [90mprivate ip[0m: 172.16.6.7
 [90m└[0m[90m──────────[0m [90margs[0m: /usr/bin/python3 /src/server.py
```

Check that it worked by listing all instances with:

<CodeTabs syncKey="cli-tool">

```bash title="unikraft"
unikraft instances list
```

```bash title="kraft"
kraft cloud instance list
```

</CodeTabs>

```text title=""
NAME                  FQDN                                      STATE    CREATED AT      IMAGE                     MEMORY   ARGS                             BOOT TIME
http-python312-w4bcp  sparkling-surf-qphxdk0j.fra.unikraft.app  running  45 seconds ago  http-python312@sha256...  512 MiB  /usr/bin/python3 /src/server.py  153794us
http-python312-juvv4  old-brook-v3bf7h7z.fra.unikraft.app       running  45 seconds ago  http-python312@sha256...  512 MiB  /usr/bin/python3 /src/server.py  154744us
http-python312-8mxq5  small-darkness-4t9y8n5s.fra.unikraft.app  running  45 seconds ago  http-python312@sha256...  512 MiB  /usr/bin/python3 /src/server.py  153646us
```

Three instances run: the original plus two replicas.

### Create instances from an existing image

In this final workflow, take the existing image and start new instances from it:

<CodeTabs syncKey="cli-tool">

```bash title="unikraft"
unikraft run --metro=fra -p 443:8080/http+tls -m 512MiB --image=<my-org>/http-python312@sha256:1b815914eb568a06ca4bbfdfb7d6cf484a9e9a0947ba8e0e0f1664d972a25bca
```

```bash title="kraft"
kraft cloud instance create \
   --start \
   --port 443:8080 \
   -M 512 \
   <my-org>/http-python312@sha256:1b815914eb568a06ca4bbfdfb7d6cf484a9e9a0947ba8e0e0f1664d972a25bca
```

</CodeTabs>

You now have a new instance created from the existing image.


## REST API reference

### List images

```
GET /images
```

Returns all images accessible for your account and known by the platform.
Each image object includes:

| Field | Type | Description |
|-------|------|-------------|
| `uuid` | string | Image UUID. |
| `created_at` | timestamp | Creation time. |
| `owner` | string | Owner of the image (omitted if not root account). |
| `url` | string | Full image address (includes digest). |
| `tags` | array of strings | List of shortened tags. |
| `initrd_or_rom` | bool | Whether the image includes an initrd or ROM component. |
| `size_in_bytes` | int64 | Total image size in bytes. |
| `args` | array of strings | Default arguments (omitted if none). |
| `env` | map of strings | Default environmental variables (omitted if none). |
| `users` | array of strings | Users with access to the image (omitted if not root account). |

### Look up by UUID

```
GET /images?uuid=<uuid>[,<uuid>…]
```

Returns one or more images by UUID.

### Look up by digest

```
GET /images
```

Pass a `digest` string in the request body to return the image matching that digest:

```json title="GET /images"
{
  "digest": "user/image@sha256:278cb8b14f9faf9c2702dddd8bfb6124912d82c11b4a2c6590b6e32fc4049472"
}
```

The response includes `url`, `tags`, `initrd_or_rom`, `size_in_bytes`, args` and `env`.

### Look up by tag

```
GET /images
```

Pass a `tag` string in the request body to return the image matching that tag:

```json title="GET /images"
{
  "tag": "user/image:tag"
}
```

The response includes `url`, `tags`, `initrd_or_rom`, `size_in_bytes`, args` and `env`.

## Learn more

* The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview).
* Unikraft Cloud's [REST API reference](/api/platform/v1).
