Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 32 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Building Unikernel Images for urunc

This guide demonstrates how to build Unikernel applications and package them as OCI container images so they can be seamlessly deployed over `urunc`.

## Prerequisites

To follow these examples, you must have the following tools available on your host system:
- **[urunc](https://urunc.io/):** The unikernel container runtime.
- **[containerd](https://containerd.io/):** The container daemon for managing the images.
- **[kraftkit](https://unikraft.org/docs/cli):** The Unikraft CLI, used to build the raw unikernel binaries.
- **Docker:** We use Docker along with the [nubificus/bunny](https://github.com/nubificus/bunny) BuildKit frontend to package the binaries.

## The Building Process

The recommended, robust way to build unikernel images for `urunc` is a two-step process. Rather than writing a monolithic Dockerfile that executes `kraft build` internally (which can cause BuildKit-in-BuildKit nesting issues), we separate the compilation from the packaging:

1. **Compile the Unikernel:** Use standard Unikraft tooling (e.g., `kraft build`) to compile your application into a unikernel binary and generate its corresponding root filesystem (`initrd`).
2. **Package with Bunny:** Create a `bunnyfile` that points to the compiled binaries. When you run `docker build -f bunnyfile`, the `bunny` frontend kicks in. It takes the binary and rootfs, packages them into a standard OCI container image, and automatically injects all the necessary `com.urunc.unikernel.*` annotations so that `urunc` knows exactly how to execute it.

## Storage Support

`urunc` supports standard container storage patterns. When you package your image using `bunny`, you specify a `rootfs` (typically a `cpio` archive) which `urunc` will mount at runtime as the base filesystem.

If your application requires persistent data (e.g., a database like Redis), you can also mount additional persistent volumes via standard Kubernetes or `nerdctl` volume mounts. These will be passed through to the unikernel.

## End-to-End Examples

We provide three complete examples of this process:

- [Nginx](../examples/nginx/README.md)
- [Redis](../examples/redis/README.md)
- [Httpreply](../examples/httpreply/README.md)
35 changes: 35 additions & 0 deletions examples/httpreply/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Httpreply Unikernel Example

This example demonstrates how to build and package a simple HTTP reply unikernel using `kraft` and `bunny`.

## Step 1: Compile the Unikernel

First, clone the Unikraft httpreply repository (or a similar simple web server example) and build the binary:

```bash
git clone https://github.com/unikraft/app-httpreply.git
cd app-httpreply
kraft build --target qemu-x86_64
```

> [!WARNING]
> The `kraft build` step MUST succeed and produce a valid kernel binary. `bunny` only packages existing files; if the compilation fails or produces an empty file, the resulting OCI image will not be able to boot in `urunc`.

## Step 2: Package with Bunny

Copy the `bunnyfile` from this directory into your project directory, along with the kernel.
Since `httpreply` is a simple unikernel, it generally does not require a complex root filesystem, so our `bunnyfile` just uses `scratch`.

For example:
```bash
cp /path/to/urunc/examples/httpreply/bunnyfile .
cp .unikraft/build/httpreply_qemu-x86_64 ./httpreply-qemu-x86_64
```

Then, build the OCI image using Docker and the `bunny` BuildKit frontend:

```bash
docker build -f bunnyfile -t urunc-httpreply:latest .
```

You now have a fully-packaged HTTP unikernel image ready to be run with `urunc`!
13 changes: 13 additions & 0 deletions examples/httpreply/bunnyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#syntax=harbor.nbfc.io/nubificus/bunny:latest
version: v0.1
platforms:
framework: unikraft
monitor: qemu
architecture: x86_64
rootfs:
from: scratch
type: raw
kernel:
from: local
path: httpreply-qemu-x86_64
cmdline: "/httpreply"
37 changes: 37 additions & 0 deletions examples/nginx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Nginx Unikernel Example

This example demonstrates how to build and package an Nginx unikernel using `kraft` and `bunny`.

## Step 1: Compile the Unikernel

First, clone the Unikraft Nginx repository and build the binary and rootfs:

```bash
git clone https://github.com/unikraft/app-nginx.git
cd app-nginx
kraft build --target qemu-x86_64
```

> [!WARNING]
> The `kraft build` step MUST succeed and produce a valid kernel binary and rootfs. `bunny` only packages existing files; if the compilation fails or produces an empty file, the resulting OCI image will not be able to boot in `urunc`.

After a successful build, you will have the compiled kernel and a root filesystem archive in the output directory.

## Step 2: Package with Bunny

Copy the `bunnyfile` from this directory into your `app-nginx` project directory, along with the kernel and rootfs so they match the paths in the `bunnyfile`.

For example:
```bash
cp /path/to/urunc/examples/nginx/bunnyfile .
cp .unikraft/build/nginx_qemu-x86_64 ./nginx-qemu-x86_64
cp .unikraft/build/initrd.cpio ./rootfs.cpio
```

Then, build the OCI image using Docker and the `bunny` BuildKit frontend:

```bash
docker build -f bunnyfile -t urunc-nginx:latest .
```

You now have a fully-packaged Nginx unikernel image ready to be run with `urunc`!
13 changes: 13 additions & 0 deletions examples/nginx/bunnyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#syntax=harbor.nbfc.io/nubificus/bunny:latest
version: v0.1
platforms:
framework: unikraft
monitor: qemu
architecture: x86_64
rootfs:
from: local
path: rootfs.cpio
kernel:
from: local
path: nginx-qemu-x86_64
cmdline: "nginx -c /nginx/conf/nginx.conf"
49 changes: 49 additions & 0 deletions examples/redis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Redis Unikernel Example

This example demonstrates how to build and package a Redis unikernel using `kraft` and `bunny`.

One of the great features of `bunny` is that it can dynamically create the `initrd` (root filesystem) for you. Instead of dealing with complex build-system hacks to embed `redis.conf`, we can simply tell `bunny` to include our local `redis.conf` in the final image!

## Step 1: Compile the Unikernel

First, clone the Unikraft Redis repository and build the binary:

```bash
git clone https://github.com/unikraft/app-redis.git
cd app-redis
kraft build --target qemu-x86_64
```

> [!WARNING]
> The `kraft build` step MUST succeed and produce a valid kernel binary. `bunny` only packages existing files; if the compilation fails or produces an empty file, the resulting OCI image will not be able to boot in `urunc`.

## Step 2: Package with Bunny

Copy the `bunnyfile` from this directory into your `app-redis` project directory, along with the kernel. Create a default `redis.conf` file as well.

For example:
```bash
cp /path/to/urunc/examples/redis/bunnyfile .
cp .unikraft/build/redis_qemu-x86_64 ./redis-qemu-x86_64

# Create a basic redis.conf
echo "port 6379" > redis.conf
```

Notice the `rootfs` section in our `bunnyfile`:
```yaml
rootfs:
from: scratch
type: initrd
include:
- redis.conf:/conf/redis.conf
```
This instructs `bunny` to generate a new `initrd` from scratch and embed our local `redis.conf` into `/conf/redis.conf`.

Finally, build the OCI image:

```bash
docker build -f bunnyfile -t urunc-redis:latest .
```

You now have a fully-packaged Redis unikernel image ready to be run with `urunc`!
15 changes: 15 additions & 0 deletions examples/redis/bunnyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#syntax=harbor.nbfc.io/nubificus/bunny:latest
version: v0.1
platforms:
framework: unikraft
monitor: qemu
architecture: x86_64
rootfs:
from: scratch
type: initrd
include:
- redis.conf:/conf/redis.conf
kernel:
from: local
path: redis-qemu-x86_64
cmdline: "redis-server /conf/redis.conf"
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ nav:
- Developer Guide:
- developer-guide/*.md
- API Reference: https://pkg.go.dev/github.com/urunc-dev/urunc/pkg/unikontainers
- Examples: examples.md
- Tutorials:
- tutorials/*.md

Expand Down