# Build/Test Environments with ROMs

import { Tabs, TabsContent, TabsList, TabsTrigger } from "zudoku/ui/Tabs"

{/* vale off */}
{/* THIS FILE WAS AUTOGENERATED FROM THE PUBLIC EXAMPLE REPOSITORY. DO NOT EDIT THIS FILE DIRECTLY. */}


This guide shows how to deploy a Go runtime that compiles ROM-provided code before execution.
The base image contains a generic server and Go toolchain.
Each ROM only contains a `rom.go` file, which the instance compiles into a Go plugin (`.so`) at startup.

This pattern is useful for build and test environments where runtime code changes frequently but the execution environment stays stable.

## Prerequisites

1. Install the CLI:
   Use the [unikraft CLI](/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install).
   You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way is via [Docker](https://docs.docker.com/engine/install/).
   Alternatively, set up and use BuildKit directly, see the [quick start](https://github.com/moby/buildkit#quick-start).

   :::note
   The unikraft CLI is the current standard, while kraft is the legacy version.
   Choose one of the CLIs below and only run the commands associated with it for the rest of this guide.
   :::

2. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/build-environments` directory:

   ```bash
   git clone https://github.com/unikraft-cloud/examples
   cd examples/build-environments/
   ```

Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you.
This guide uses `fra` (Frankfurt, 🇩🇪):

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft login
```

```bash title="kraft"
# Set Unikraft Cloud access token
export UKC_TOKEN=token
export UKC_METRO=fra
```

</CodeTabs>

## Deployment Workflow

### Package the base image

Package and push the base Go runtime image (see `server.go` for the runtime implementation):

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft build . --output <my-org>/go-build-env:latest
```

```bash title="kraft"
kraft pkg \
  --name index.unikraft.io/<my-org>/go-build-env:latest \
  --plat kraftcloud \
  --arch x86_64 \
  --rootfs-type erofs \
  --push \
  .
```

</CodeTabs>

The server in [`server.go`](./server.go) loads `/rom/rom.go`, compiles it to `/run/rom.so` using `go build -buildmode=plugin`, and invokes `Handler()` from the plugin.

### Create an instance template from the base image

Create a short-lived instance from the base image (without ROM attached).
The server writes to `/uk/libukp/template_instance` and turns the instance into a template before serving requests (see /platform/instances#instance-templates):

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft run --metro fra \
  --name go-build-env \
  -m 512M \
  --image <my-org>/go-build-env:latest
```

```bash title="kraft"
kraft cloud instance create \
  --start \
  --name go-build-env \
  -M 512Mi \
  <my-org>/go-build-env:latest
```

</CodeTabs>

The output shows the instance details:

<CodeTabs syncKey="cli">

```ansi title="unikraft"
[1mmetro[22m:        fra
[1mname[22m:         go-build-env
[1muuid[22m:         650dbbe7-3949-4c93-88e7-6619a9216e0c
[1mstate[22m:        [38;2;144;161;185mstarting[0m
[1mimage[22m:        <my-org>/go-build-env
[1mresources[22m:
[3m  memory[23m:     512MiB
[3m  vcpus[23m:      1
[1mnetworks[22m:
[3m- uuid[23m:       6f7a8b9c-0d1e-2f3a-4b5c-f6a7b8c9d0e1
[3m  private-ip[23m: 10.0.5.4
[3m  mac[23m:        12:b0:6c:3e:ab:95
[1mtimestamps[22m:
[3m  created[23m:    just now
```

```ansi title="kraft"
[38;5;245m[[0m[92m●[0m[38;5;245m][0m Deployed successfully!
 [38;5;245m│[0m
 [38;5;245m├[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m [38;5;245mname[0m: go-build-env
 [38;5;245m├[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m [38;5;245muuid[0m: 650dbbe7-3949-4c93-88e7-6619a9216e0c
 [38;5;245m├[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m [38;5;245mmetro[0m: https://api.fra.unikraft.cloud/v1
 [38;5;245m├[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m [38;5;245mstate[0m: [92mstarting[0m
 [38;5;245m├[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m [38;5;245mimage[0m: oci://unikraft.io/<my-org>/go-build-env@sha256:1f57e9bb8702d031743acf43164b24cf182158c398f1eda8c5583208ccc9c300
 [38;5;245m├[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m [38;5;245mmemory[0m: 512 MiB
 [38;5;245m├[0m[38;5;245m─[0m [38;5;245mprivate fqdn[0m: go-build-env.internal
 [38;5;245m└[0m[38;5;245m─[0m[38;5;245m─[0m[38;5;245m─[0m [38;5;245mprivate ip[0m: 10.0.5.4
```

</CodeTabs>

If you are fast enough, you can list information about the instance by running:

<CodeTabs syncKey="cli">

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

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

</CodeTabs>

This instance is short-lived, since right before the server starts, it triggers a conversion into a template.
To check the template is ready, run:

<CodeTabs syncKey="cli">

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

```bash title="unikraft"
[1mMETRO[22m  [1mNAME[22m          [1mSTATE[22m     [1mIMAGE[22m                  [1mARGS[22m  [1mMEMORY[22m  [1mVCPUS[22m  [1mCREATED[22m
fra    go-build-env  [38;2;43;127;255mtemplate[0m  <my-org>/go-build-env        512MiB  1      2 minutes ago
```

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

```bash title="kraft"
[0;1;39mNAME[0m          [0;1;39mIMAGE[0m                                                                                                            [0;1;39mARGS[0m  [0;1;39mCREATED AT[0m
go-build-env  oci://unikraft.io/<my-org>/go-build-env@sha256:1f57e9bb8702d031743acf43164b24cf182158c398f1eda8c5583208ccc9c300        3 minutes ago
```

</CodeTabs>

### Package the ROMs

Each ROM contains a Go function implementation (see [`rom1/fs/rom.go`](./rom1/fs/rom.go) and [`rom2/fs/rom.go`](./rom2/fs/rom.go)).

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft build rom1/ --output <my-org>/go-rom1:latest
unikraft build rom2/ --output <my-org>/go-rom2:latest
```

```bash title="kraft"
kraft pkg \
  --name index.unikraft.io/<my-org>/go-rom1:latest \
  --rom ./fs \
  --rom-type erofs \
  --plat kraftcloud \
  --arch x86_64 \
  --push \
  rom1/
kraft pkg \
  --name index.unikraft.io/<my-org>/go-rom2:latest \
  --rom ./fs \
  --rom-type erofs \
  --plat kraftcloud \
  --arch x86_64 \
  --push \
  rom2/
```

</CodeTabs>

### Create instances from the template with different ROMs attached

Create an instance with the first ROM:

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft run --metro fra \
  --name go-build-env-rom1 \
  -p 443:8080/tls+http \
  --scale-to-zero policy=on,cooldown-time=1000,stateful=true \
  --rom image=<my-org>/go-rom1:latest,at=/rom \
  --template go-build-env
```

```bash title="kraft"
# kraft does not support creating instances with attached ROMs, but you can use the API directly
curl -X POST "$UKC_METRO/instances" \
   -H "Accept: application/json" \
   -H "Authorization: Bearer $UKC_TOKEN" \
   -H "Content-Type: application/json" \
   -d '{
   "name": "go-build-env-rom1",
   "template": {
      "name": "go-build-env"
   },
   "autostart": true,
   "service_group": {
      "services": [
         {
            "port": 443,
            "destination_port": 8080,
            "handlers": ["tls", "http"]
         }
      ]
   },
   "scale_to_zero": {
      "policy": "on",
      "stateful": true,
      "cooldown_time_ms": 1000
   },
   "roms": [
      {
         "name": "go_function",
         "image": "index.unikraft.io/<my-org>/go-rom1:latest",
         "at": "/rom"
      }
   ]
}'
```

</CodeTabs>

The instance will compile the ROM into a plugin on first start, which may take a few seconds.
To check the progress, you can view the instance logs:

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft instances logs go-build-env-rom1 -f
```

```bash title="kraft"
kraft cloud instance logs go-build-env-rom1 -f
```

</CodeTabs>

Create another instance with the second ROM:

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft run --metro fra \
  --name go-build-env-rom2 \
  -p 443:8080/tls+http \
  --scale-to-zero policy=on,cooldown-time=1000,stateful=true \
  --rom image=<my-org>/go-rom2:latest,at=/rom \
  --template go-build-env
```

```bash title="kraft"
# kraft does not support creating instances with attached ROMs, but you can use the API directly
curl -X POST "$UKC_METRO/instances" \
   -H "Accept: application/json" \
   -H "Authorization: Bearer $UKC_TOKEN" \
   -H "Content-Type: application/json" \
   -d '{
   "name": "go-build-env-rom2",
   "template": {
      "name": "go-build-env"
   },
   "autostart": true,
   "service_group": {
      "services": [
         {
            "port": 443,
            "destination_port": 8080,
            "handlers": ["tls", "http"]
         }
      ]
   },
   "scale_to_zero": {
      "policy": "on",
      "stateful": true,
      "cooldown_time_ms": 1000
   },
   "roms": [
      {
         "name": "go_function",
         "image": "index.unikraft.io/<my-org>/go-rom2:latest",
         "at": "/rom"
      }
   ]
}'
```

</CodeTabs>

The instance will compile the ROM into a plugin on first start, which may take a few seconds.
To check the progress, you can view the instance logs:

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft instances logs go-build-env-rom2 -f
```

```bash title="kraft"
kraft cloud instance logs go-build-env-rom2 -f
```

</CodeTabs>

List the instances and note their FQDN values:

<CodeTabs syncKey="cli">

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

```bash title="unikraft"
[1mMETRO[22m  [1mNAME[22m               [1mSTATE[22m    [1mIMAGE[22m                  [1mARGS[22m  [1mMEMORY[22m  [1mVCPUS[22m  [1mFQDN[22m                                      [1mCREATED[22m
fra    go-build-env-rom2  [38;2;43;127;255mstandby[0m  <my-org>/go-build-env        512MiB  1      nameless-wood-gw7pbnls.fra.unikraft.app   2 minutes ago
fra    go-build-env-rom1  [38;2;43;127;255mstandby[0m  <my-org>/go-build-env        512MiB  1      sparkling-dawn-syowlbtj.fra.unikraft.app  3 minutes ago
```

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

```bash title="kraft"
[0;1;39mNAME[0m               [0;1;39mFQDN[0m                                      [0;1;39mSTATE[0m    [0;1;39mSTATUS[0m   [0;1;39mIMAGE[0m                                                     [0;1;39mMEMORY[0m   [0;1;39mVCPUS[0m  [0;1;39mARGS[0m  [0;1;39mBOOT TIME[0m
go-build-env-rom2  nameless-wood-gw7pbnls.fra.unikraft.app   [0;36mstandby[0m  standby  oci://unikraft.io/<my-org>/go-build-env@sha256:1cbd64...  512 MiB  1            6.98 ms
go-build-env-rom1  sparkling-dawn-syowlbtj.fra.unikraft.app  [0;36mstandby[0m  standby  oci://unikraft.io/<my-org>/go-build-env@sha256:1cbd64...  512 MiB  1            7.86 ms
```

</CodeTabs>

Test both instances:

```bash
curl https://sparkling-dawn-syowlbtj.fra.unikraft.app
curl https://nameless-wood-gw7pbnls.fra.unikraft.app
```

```text
Bye, World!
Auf Wiedersehen!
```

## Cleanup

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft instances delete go-build-env-rom1 go-build-env-rom2
unikraft instances template delete go-build-env
```

```bash title="kraft"
kraft cloud instance remove go-build-env-rom1 go-build-env-rom2
kraft cloud instance template remove go-build-env
```

</CodeTabs>

## Learn more

Use the `--help` option for detailed information on using Unikraft Cloud:

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft --help
```

```bash title="kraft"
kraft cloud --help
```

</CodeTabs>

Or visit the [CLI Reference](/cli/unikraft) or the [legacy CLI Reference](/cli/kraft/overview).
{/* vale on */}
