Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9e660a0
feat: Added target to all kraftfiles examples
dragosgheorghioiu Aug 17, 2026
8ba5bab
feat: Added target to all kraftfiles examples (#254)
danielvallance Aug 21, 2026
1143ff1
chore: Update sync workflow to include more examples
danielvallance Aug 21, 2026
6e9b665
chore: Update sync workflow to include more examples (#264)
danielvallance Aug 21, 2026
e72fe11
chore: Fix broken/missing links in guides/overview
danielvallance Aug 21, 2026
fa3f0b0
chore: Fix broken/missing links in guides/overview (#265)
danielvallance Aug 21, 2026
70d362f
fix(tagging): Correct the tag-at-creation examples
jedevc Aug 20, 2026
7c5ab08
fix: Use singular --service and --domain flags
jedevc Aug 20, 2026
d95edd3
docs(cli): Document list and map fields
jedevc Aug 20, 2026
9508f80
chore: Fix faulty example in platform/services
aabedraba Aug 25, 2026
1a5c73a
chore: Fix Tailwind failure at runtime
aabedraba Aug 25, 2026
7e48007
chore: Fix faulty example in platform/services (#267)
danielvallance Aug 25, 2026
9b838a6
chore: Use native CLI commands for branching/checkpointing
jedevc Aug 26, 2026
0bf0871
chore: Use native CLI commands for branching/checkpointing (#268)
danielvallance Aug 27, 2026
2356dc3
chore: Update limitations in checkpoint/branching docs
danielvallance Aug 27, 2026
55c2b1a
chore: Remove deprecated -d flag in unikraft api calls
danielvallance Aug 27, 2026
e925da3
chore: Update limitations in checkpoint/branching docs (#270)
danielvallance Aug 27, 2026
4a86fac
chore: Update full vm/gpu docs with new CLI
danielvallance Aug 27, 2026
5f339ff
chore: Update full vm/gpu docs with new CLI (#271)
danielvallance Aug 27, 2026
1e49a1e
chore: Remove deprecated -d flag in unikraft api calls (#272)
danielvallance Aug 27, 2026
0a082fe
fix: Catch up docs with the CLI flag renames (#261)
danielvallance Aug 28, 2026
7507ed6
feat: Document multi-arch builds
danielvallance Aug 27, 2026
73bf915
feat: Document multi-arch builds (#269)
danielvallance Aug 30, 2026
dedfbab
docs: Scheduled checkpoints
danielvallance Aug 28, 2026
b34f3f4
docs: Plugin authentication
danielvallance Aug 28, 2026
a3395b0
docs: Specify MAC address on interfaces
danielvallance Aug 28, 2026
a4c5d96
docs: Scheduled checkpoints (#274)
danielvallance Aug 31, 2026
fd3562a
docs: Plugin authentication (#276)
danielvallance Aug 31, 2026
98c8ddb
feat: Add high watermark section
dragosgheorghioiu Aug 31, 2026
6685137
docs(autoscale): Add high watermark section (#277)
danielvallance Aug 31, 2026
11ec846
docs: Specify MAC address on interfaces (#275)
dragosgheorghioiu Aug 31, 2026
dca584a
docs: Nested virtualisation
danielvallance Aug 31, 2026
9f01ba8
docs: Karpenter integration
danielvallance Aug 31, 2026
c87d42e
feat: Add plugin sdk page
dragosgheorghioiu Aug 31, 2026
f16665a
docs: Nested virtualisation (#282)
dragosgheorghioiu Aug 31, 2026
491ddf1
feat: Add plugin sdk page (#281)
dragosgheorghioiu Aug 31, 2026
65cec05
docs: Karpenter integration (#280)
danielvallance Aug 31, 2026
d2519e5
docs: Instance annotation events
danielvallance Aug 31, 2026
a3fda91
docs: Kernel-less images
danielvallance Aug 31, 2026
8afdc20
docs: Kernel-less images (#283)
dragosgheorghioiu Sep 1, 2026
8c6b8d3
docs: Instance annotation events (#284)
danielvallance Sep 1, 2026
671d22d
docs: Guest network configuration via CNI
danielvallance Aug 28, 2026
d3750ac
feat: Add official plugin section
dragosgheorghioiu Sep 1, 2026
16a116f
feat: Add official plugin section (#285)
danielvallance Sep 1, 2026
2a1f471
docs: Make enterprise-gated features more explicit
danielvallance Sep 1, 2026
9598487
docs: Make enterprise-gated features more explicit (#286)
dragosgheorghioiu Sep 1, 2026
b2ee618
docs: Guest network configuration via CNI (#278)
dragosgheorghioiu Sep 1, 2026
2fba2e0
docs: JS SDK
danielvallance Sep 2, 2026
e0d23ee
docs: JS SDK (#288)
danielvallance Sep 2, 2026
cbe5e4e
docs: Correction regarding the source of TAP devices
danielvallance Sep 2, 2026
b042f08
fix: Replace --domains/--services with --domain/--service
danielvallance Sep 2, 2026
1a8a552
docs: Correction regarding the source of TAP devices (#289)
dragosgheorghioiu Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/actions/setup-www/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ runs:
if [[ $(unikraft services list --filter "name==${{ inputs.name }},metro==${{ inputs.metro }}" -o raw | jq 'length') -ne 1 ]]; then
if [[ "${{ inputs.domain }}" == "" ]]; then
unikraft services create --metro "${{ inputs.metro }}" \
--domains "${{ inputs.name }}" \
--domain "${{ inputs.name }}" \
--name "${{ inputs.name }}" \
--services "443:${{ inputs.port }}/http+tls" \
--service "443:${{ inputs.port }}/http+tls" \
;
else
unikraft services create --metro "${{ inputs.metro }}" \
--domains "${{ inputs.domain }}" \
--domain "${{ inputs.domain }}" \
--name "${{ inputs.name }}" \
--services "443:${{ inputs.port }}/http+tls" \
--service "443:${{ inputs.port }}/http+tls" \
;
fi
fi
Expand Down
5 changes: 5 additions & 0 deletions .vale/styles/config/vocabularies/Unikraft/accept.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ FUSE
(?i)webservers?
ALIAS
ANAME
ARM
ASCII
BYOC
CNAME
Expand All @@ -41,6 +42,7 @@ OCI
OLAP
OOM
PORT
QEMU
REST
RUN
SNI
Expand Down Expand Up @@ -71,6 +73,9 @@ CSI
CNI
VPC
XFS
MAC
IPAM
BPF

# ==============================================================================
# NAMES OF THINGS
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ sync:
readme="$$example/README.md"; \
if [ -f "$$readme" ]; then \
# TODO: remove - Only transform updated READMEs \
if grep -q "# Set metro to Frankfurt, DE" "$$readme" ; then \
if grep -q "Make sure to log into Unikraft Cloud" "$$readme" ; then \
guide="$(GUIDES_DIR)/$$name.mdx" ;\
echo " 馃搫 $$name -> $$(basename $$guide)" ;\
$(WORKDIR)/scripts/transform_readme.py "$$readme" "$$guide" "$$name" ;\
Expand Down
17 changes: 17 additions & 0 deletions pages/cli/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,23 @@ The `--add` flag works the same way, appending the given value instead of removi

You can combine `--set`, `--add`, and `--del` flags in a single `edit` invocation.

### List and map fields

Each flag carries exactly one element, so a field holding a list or a map takes one flag per entry.
The CLI never splits a value on commas鈥攁 comma inside a value is part of that value.

```bash
unikraft instances edit httpserver-rust175-tokio \
--set tags=production \
--set tags=customer_A
```

To pass a whole list or map in a single flag, write it as JSON instead.

```bash
unikraft instances edit httpserver-rust175-tokio --set tags='["production","customer_A"]'
```

## Visual editing

Pass `--visual` to open the resource in your editor and edit its fields interactively.
Expand Down
3 changes: 2 additions & 1 deletion pages/cli/registries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,15 @@ kraft cloud image rm <my-org>/<my-app>[:<tag>]

## Kernel layer deduplication

An image has two OCI layers: a kernel layer and a rootfs layer.
An image typically has two OCI layers: a kernel layer and a rootfs layer.
When you push an update to an existing image, the CLI checks whether the kernel layer is already present in the registry and skips re-uploading it.
Only the rootfs layer travels over the wire.
This makes iterative pushes faster and cheaper on bandwidth.

[ROMs](/features/roms) follow the same packaging system but go out without a kernel layer.
They only have a rootfs.
They appear in the image list alongside regular images.
Images that boot on the [platform kernel](/platform/images#platform-kernel) carry a rootfs layer alone in the same way.

## Instance creation

Expand Down
29 changes: 28 additions & 1 deletion pages/faq.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ spec: v0.7

runtime: python:3.12

targets:
- kraftcloud/x86_64

rootfs:
source: ./Dockerfile
format: erofs
Expand All @@ -98,6 +101,11 @@ cmd: ["/usr/bin/python3", "/src/server.py"]
The `runtime` specifies one of the Unikraft Cloud runtimes (microVMs) built to run different languages and apps.
Here it specifies a Python runtime.

The `targets` parameter specifies the platform and architecture pairs to build for.
Here it targets `kraftcloud` (Unikraft Cloud) on `x86_64`, which is the architecture the hosted platform runs on.
No architecture is the default, so list every one you need, for example `kraftcloud/arm64` alongside `kraftcloud/x86_64`.
See [architectures](/platform/images#architectures) for more detail.

The `rootfs` parameter tells the CLI to use a `Dockerfile` in the same directory to build the root filesystem as an [EROFS image](/tutorials/rootfs-formats).

The `cmd` parameter tells the platform which command to run when deployed.
Expand Down Expand Up @@ -212,6 +220,23 @@ The CLI can run on Mac and Linux out of the box.
Windows isn't officially supported, but you can use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) to run the CLI on Windows.


{/* vale off */}
#### What CPU architectures can you deploy to?
{/* vale on */}

Both `x86_64` and `arm64`, and the hosted platform runs on `x86_64`.
The architecture of the guest image must match the host architecture, so name the architectures you deploy to in the `targets` key of your `Kraftfile` or through the `--arch` flag.
Neither architecture is the default, and a single `unikraft build` invocation produces an image for every architecture you ask for.
See [architectures](/platform/images#architectures) for more detail.

{/* vale off */}
:::caution[**Limited Access**]
`arm64` hosts are available as part of enterprise plans.
To deploy to them, reach out to the [Unikraft Cloud Discord](https://kraft.cloud/discord) or send an email to [support@unikraft.com](mailto:support@unikraft.com).
:::
{/* vale on */}


#### How can you track instances on Unikraft Cloud?

Unikraft provides full `stdout` and `stderr` log support for all users.
Expand Down Expand Up @@ -296,13 +321,15 @@ You should first experiment with this or discuss it with a Unikraft Engineer to

#### What can you push as an image?

An image is specifically a pair of a kernel and a rootfs bundled together as different layers of an OCI package.
An image typically bundles a kernel and a rootfs together as different layers of an OCI package.
To keep things light, certain optimizations are in place to not repush existing kernels and to reuse them from your account.
If these aren't automatically detected, the CLI pushes the kernel again.

At the same time, things like [ROMs](/features/roms) use the same packaging systems, but push images with a missing kernel and only a rootfs.
These have specific metadata that identifies them as ROMs.

An image can also ship a rootfs alone and boot on the kernel installed on the node instead of one of its own, which the [platform kernel](/platform/images#platform-kernel) section covers.

Finally, kernels can also run without a rootfs.
They don't do anything, but support exists to run them.

Expand Down
126 changes: 117 additions & 9 deletions pages/features/annotations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ navigation_icon: sticky-note
{/* vale off */}
:::caution[**Limited Access**]
Instance annotations are available as part of enterprise plans.
To enable them for your account, reach out to the [Unikraft Cloud Discord](https://kraft.cloud/discord) or send an email to [support@unikraft.com](mailto:support@unikraft.com).
To try them out, reach out to the [Unikraft Cloud Discord](https://kraft.cloud/discord) or send an email to [support@unikraft.com](mailto:support@unikraft.com).
:::
{/* vale on */}

**Annotations** attach arbitrary key-value metadata to an instance.
They use the same key syntax as [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/), so you can reuse your existing keys and tooling.

Unlike [tags](/platform/tagging), which act as controller-level labels that only the platform sees, annotations also reach the guest.
You can use them for two things:
You can use them for three things:

{/* vale off */}
1. **Custom metadata**鈥攖he instance's startdata includes its annotations, so the guest can read them at runtime.
2. **Structured log output**鈥攜ou select which annotations the platform injects into the VM's console log output.
2. **Guest network configuration**鈥攖he guest applies the [CNI result](#guest-network-configuration-with-cni) in the `unikraft.com/cni` annotation to its network interfaces.
3. **Structured log output**鈥攜ou select which annotations the platform injects into the VM's console log output.
{/* vale on */}

{/* vale off */}
Expand All @@ -30,7 +31,9 @@ In the meantime, drive annotations through the [API](/api/platform/v1/instances)

## Annotation keys

An annotation is a set of key-value pairs, and values can be any string.
An annotation is a set of key-value pairs.
A value is any string without ASCII control characters (`0x00`鈥揱0x1f` and `0x7f`, which covers tab, newline and carriage return), and multi-byte UTF-8 goes through fine.
The API rejects a value carrying an ASCII control character with `400 Bad Request`.
Keys follow the [Kubernetes annotation key syntax](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set):

- A key can be a plain name (`my-key`) or carry an optional DNS prefix separated by a slash (`example.com/annotation2`).
Expand Down Expand Up @@ -58,7 +61,7 @@ Each entry is a key-value pair:

```bash title="unikraft"
unikraft api /v1/instances -X POST --metro fra \
-d '{
'{
"name": "annotations-demo",
"image": "<my-org>/annotations-demo:latest",
"annotations": {
Expand Down Expand Up @@ -98,6 +101,88 @@ cat /sys/class/uio/uio0/device/startdata

Your app parses this JSON and reads the `annotations` object like any other startdata field.

## Guest network configuration with CNI

`unikraft.com/cni` is a reserved annotation.
Its value is a [CNI Result](https://www.cni.dev/docs/spec/#section-5-result-types) document, and the guest applies the addresses it holds to its own network interfaces.

This hands the addressing of an instance to an external [CNI](https://www.cni.dev/) plugin or IPAM system.
The plugin decides the addresses, writes its result into the annotation, and the guest configures itself from it.
Your image needs no agent for it and no boot script of its own.

The annotation pairs with an [interface without an address](/features/custom-network-configuration#interfaces-without-an-address), which reaches the guest as a bare link on a TAP device of your own.
See [Kubernetes](/integrations/kubernetes#custom-networking-with-cni) for the CNI integration that Kraftlet drives on the cluster side.

The annotation value is the document as a JSON string, so the JSON inside it carries escapes:

<CodeTabs syncKey="cli">

```bash title="unikraft"
unikraft api /v1/instances -X POST --metro fra \
'{
"name": "cni-demo",
"image": "<my-org>/cni-demo:latest",
"network_interfaces": [
{},
{
"name": "cni-demo-vpc",
"tap_name": "vpc0",
"mac": "0a:58:c0:a8:01:05",
"autoconfig": false
}
],
"annotations": {
"unikraft.com/cni": "{\"cniVersion\":\"1.0.0\",\"interfaces\":[{\"name\":\"eth0\",\"mac\":\"0a:58:c0:a8:01:05\"}],\"ips\":[{\"interface\":0,\"address\":\"192.168.1.5/24\"}]}"
}
}'
```

</CodeTabs>

That instance keeps a pool-allocated primary interface and adds a second one on the `vpc0` TAP device, with a MAC address you pick and no IP address.
Unescaped, the annotation holds a standard CNI Result (`types/100`):

```json
{
"cniVersion": "1.0.0",
"interfaces": [
{ "name": "eth0", "mac": "0a:58:c0:a8:01:05" }
],
"ips": [
{ "interface": 0, "address": "192.168.1.5/24" }
]
}
```

The guest gives `192.168.1.5/24` to the link whose MAC address is `0a:58:c0:a8:01:05`, and brings that link up.

### Writing the document

Give every address a MAC address to land on:

- Each `ips[]` entry points through its `interface` index at an entry of the document's own `interfaces[]` array, not at the instance's `network_interfaces`.
An entry with no `interface` field uses index `0`.
- That `interfaces[]` entry supplies the MAC address of the interface to configure, so `interfaces[].name` can say anything.
Without a MAC address to match, the index counts the instance's non-loopback interfaces from `0` instead.
The entry carries no `mac`, the document holds no `interfaces[]` array, or the index sits past the end of that array.
- Set the MAC address yourself when you create the instance, or read it from the [instance status](/api/platform/v1/instances#get-instance-by-uuid).
- Addresses take CIDR notation, IPv4 or IPv6, and the prefix length is mandatory.
- The document needs a `cniVersion` of `0.3.1`, `0.4.0`, `1.0.0`, or `1.1.0`, and has to be a result rather than a network configuration.
- Keep the document on one line, because annotation values reject control characters.

Only `interfaces[].mac`, `ips[].interface`, and `ips[].address` have any effect.
`routes`, `gateway`, `dns`, `mtu`, and `sandbox` do nothing, so you can pass a plugin's full result through untouched.
Set the default gateway and the resolver with the instance's own `gateway` and `nameserver` fields.

### Changing addresses

Running instances pick up updates to their annotations, and reconcile them against what's currently configured, so an address change doesn't require a restart.

### When nothing happens

A single MAC address that matches no interface stops the whole document from applying, leaving every interface untouched.
Check that each MAC address in the document matches one on the instance, then read the [instance logs](/platform/instances#instance-logs), which name any document that fails to parse or apply.

## Patching annotations

Update the annotations on an existing instance with a [`PATCH /instances`](/api/platform/v1/instances) request.
Expand All @@ -116,7 +201,7 @@ If you patch a running instance, the platform accepts the request and queues the

```bash title="unikraft"
unikraft api /v1/instances -X PATCH --metro fra \
-d '[{
'[{
"name": "annotations-demo",
"prop": "annotations",
"op": "set",
Expand All @@ -138,7 +223,7 @@ It overwrites keys that already exist and leaves the other keys untouched:

```bash title="unikraft"
unikraft api /v1/instances -X PATCH --metro fra \
-d '[{
'[{
"name": "annotations-demo",
"prop": "annotations",
"op": "add",
Expand All @@ -159,7 +244,7 @@ The value is an array of key names (or a single key as a string):

```bash title="unikraft"
unikraft api /v1/instances -X PATCH --metro fra \
-d '[{
'[{
"name": "annotations-demo",
"prop": "annotations",
"op": "del",
Expand Down Expand Up @@ -204,16 +289,39 @@ When the guest writes to the app console (for example, `echo "blub" > /dev/vport
}
```

## Annotation events

The platform's event log carries a `vm.annotate` event, which it emits whenever an instance's annotations change:

```json
{
"type": "vm.annotate",
"timestamp": "2026-08-19T10:04:11Z",
"data": {
"vm": "b495f451-0370-4d8e-8b94-a5b22d2212f9",
"annotations": { "env": "prod", "team": "search" }
}
}
```

An external system consumes the event to keep track of the instance's annotation values, without polling the instance API.
The event also gives log processing a way to map an instance UUID back to its annotations.

## Limitations

- An instance holds at most 256 annotations.
- Annotation keys must follow the [Kubernetes key syntax](#annotation-keys), and values are strings.
- Annotation keys must follow the [Kubernetes key syntax](#annotation-keys), and values are strings with no ASCII control characters in them.
- A [patched](#patching-annotations) annotation change applies once the instance reaches the `stopped` state.
- A [`unikraft.com/cni`](#guest-network-configuration-with-cni) document holds at most eight interfaces and 16 addresses, and an `ips[].interface` index stays below eight.
- The guest applies only `interfaces[].mac`, `ips[].interface` and `ips[].address` from that document, and needs a recent base-compat guest kernel to apply it at all.
- Dedicated `unikraft` CLI subcommands aren't available yet, so drive annotations through the [API](/api/platform/v1/instances).
- The platform sets up [log-output](#annotations-in-log-output) forwarding at the node level, not through the instances API.
- The [`vm.annotate` event](#annotation-events) needs switching on in the node's event configuration.

## Learn more

* [Tags](/platform/tagging): controller-level labels for organizing platform resources.
* [Custom network configuration](/features/custom-network-configuration): bring your own interfaces, MAC addresses and TAP devices, including [interfaces without an address](/features/custom-network-configuration#interfaces-without-an-address) for a CNI plugin to fill in.
* [Kubernetes](/integrations/kubernetes#custom-networking-with-cni): how Kraftlet runs the CNI plugins of your cluster for Unikraft Cloud instances.
* [Instances](/platform/instances): create and manage instances.
* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the section on [instances](/api/platform/v1/instances).
2 changes: 1 addition & 1 deletion pages/features/autokill.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ curl -X POST \

```bash title="unikraft"
unikraft api /v1/instances -X PATCH --metro fra \
-d '[{
'[{
"name": "<instance-name>",
"prop": "autokill",
"op": "set",
Expand Down
Loading
Loading