Skip to content

Commit 1d56db3

Browse files
committed
Docs: Add reproducible Unikernel examples and build documentation (Nginx, Redis, Httpreply)
Signed-off-by: Nachiket Roy <nachiket.roy.2@gmail.com>
1 parent e2b45cd commit 1d56db3

8 files changed

Lines changed: 195 additions & 0 deletions

File tree

docs/examples.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Building Unikernel Images for urunc
2+
3+
This guide demonstrates how to build Unikernel applications and package them as OCI container images so they can be seamlessly deployed over `urunc`.
4+
5+
## Prerequisites
6+
7+
To follow these examples, you must have the following tools available on your host system:
8+
- **[urunc](https://urunc.io/):** The unikernel container runtime.
9+
- **[containerd](https://containerd.io/):** The container daemon for managing the images.
10+
- **[kraftkit](https://unikraft.org/docs/cli):** The Unikraft CLI, used to build the raw unikernel binaries.
11+
- **Docker:** We use Docker along with the [nubificus/bunny](https://github.com/nubificus/bunny) BuildKit frontend to package the binaries.
12+
13+
## The Building Process
14+
15+
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:
16+
17+
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`).
18+
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.
19+
20+
## Storage Support
21+
22+
`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.
23+
24+
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.
25+
26+
## End-to-End Examples
27+
28+
We provide three complete examples of this process:
29+
30+
- [Nginx](../examples/nginx/README.md)
31+
- [Redis](../examples/redis/README.md)
32+
- [Httpreply](../examples/httpreply/README.md)

examples/httpreply/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Httpreply Unikernel Example
2+
3+
This example demonstrates how to build and package a simple HTTP reply unikernel using `kraft` and `bunny`.
4+
5+
## Step 1: Compile the Unikernel
6+
7+
First, clone the Unikraft httpreply repository (or a similar simple web server example) and build the binary:
8+
9+
```bash
10+
git clone https://github.com/unikraft/app-httpreply.git
11+
cd app-httpreply
12+
kraft build --target qemu-x86_64
13+
```
14+
15+
> [!WARNING]
16+
> 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`.
17+
18+
## Step 2: Package with Bunny
19+
20+
Copy the `bunnyfile` from this directory into your project directory, along with the kernel.
21+
Since `httpreply` is a simple unikernel, it generally does not require a complex root filesystem, so our `bunnyfile` just uses `scratch`.
22+
23+
For example:
24+
```bash
25+
cp /path/to/urunc/examples/httpreply/bunnyfile .
26+
cp .unikraft/build/httpreply_qemu-x86_64 ./httpreply-qemu-x86_64
27+
```
28+
29+
Then, build the OCI image using Docker and the `bunny` BuildKit frontend:
30+
31+
```bash
32+
docker build -f bunnyfile -t urunc-httpreply:latest .
33+
```
34+
35+
You now have a fully-packaged HTTP unikernel image ready to be run with `urunc`!

examples/httpreply/bunnyfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#syntax=harbor.nbfc.io/nubificus/bunny:latest
2+
version: v0.1
3+
platforms:
4+
framework: unikraft
5+
monitor: qemu
6+
architecture: x86_64
7+
rootfs:
8+
from: scratch
9+
type: raw
10+
kernel:
11+
from: local
12+
path: httpreply-qemu-x86_64
13+
cmdline: "/httpreply"

examples/nginx/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Nginx Unikernel Example
2+
3+
This example demonstrates how to build and package an Nginx unikernel using `kraft` and `bunny`.
4+
5+
## Step 1: Compile the Unikernel
6+
7+
First, clone the Unikraft Nginx repository and build the binary and rootfs:
8+
9+
```bash
10+
git clone https://github.com/unikraft/app-nginx.git
11+
cd app-nginx
12+
kraft build --target qemu-x86_64
13+
```
14+
15+
> [!WARNING]
16+
> 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`.
17+
18+
After a successful build, you will have the compiled kernel and a root filesystem archive in the output directory.
19+
20+
## Step 2: Package with Bunny
21+
22+
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`.
23+
24+
For example:
25+
```bash
26+
cp /path/to/urunc/examples/nginx/bunnyfile .
27+
cp .unikraft/build/nginx_qemu-x86_64 ./nginx-qemu-x86_64
28+
cp .unikraft/build/initrd.cpio ./rootfs.cpio
29+
```
30+
31+
Then, build the OCI image using Docker and the `bunny` BuildKit frontend:
32+
33+
```bash
34+
docker build -f bunnyfile -t urunc-nginx:latest .
35+
```
36+
37+
You now have a fully-packaged Nginx unikernel image ready to be run with `urunc`!

examples/nginx/bunnyfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#syntax=harbor.nbfc.io/nubificus/bunny:latest
2+
version: v0.1
3+
platforms:
4+
framework: unikraft
5+
monitor: qemu
6+
architecture: x86_64
7+
rootfs:
8+
from: local
9+
path: rootfs.cpio
10+
kernel:
11+
from: local
12+
path: nginx-qemu-x86_64
13+
cmdline: "nginx -c /nginx/conf/nginx.conf"

examples/redis/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Redis Unikernel Example
2+
3+
This example demonstrates how to build and package a Redis unikernel using `kraft` and `bunny`.
4+
5+
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!
6+
7+
## Step 1: Compile the Unikernel
8+
9+
First, clone the Unikraft Redis repository and build the binary:
10+
11+
```bash
12+
git clone https://github.com/unikraft/app-redis.git
13+
cd app-redis
14+
kraft build --target qemu-x86_64
15+
```
16+
17+
> [!WARNING]
18+
> 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`.
19+
20+
## Step 2: Package with Bunny
21+
22+
Copy the `bunnyfile` from this directory into your `app-redis` project directory, along with the kernel. Create a default `redis.conf` file as well.
23+
24+
For example:
25+
```bash
26+
cp /path/to/urunc/examples/redis/bunnyfile .
27+
cp .unikraft/build/redis_qemu-x86_64 ./redis-qemu-x86_64
28+
29+
# Create a basic redis.conf
30+
echo "port 6379" > redis.conf
31+
```
32+
33+
Notice the `rootfs` section in our `bunnyfile`:
34+
```yaml
35+
rootfs:
36+
from: scratch
37+
type: initrd
38+
include:
39+
- redis.conf:/conf/redis.conf
40+
```
41+
This instructs `bunny` to generate a new `initrd` from scratch and embed our local `redis.conf` into `/conf/redis.conf`.
42+
43+
Finally, build the OCI image:
44+
45+
```bash
46+
docker build -f bunnyfile -t urunc-redis:latest .
47+
```
48+
49+
You now have a fully-packaged Redis unikernel image ready to be run with `urunc`!

examples/redis/bunnyfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#syntax=harbor.nbfc.io/nubificus/bunny:latest
2+
version: v0.1
3+
platforms:
4+
framework: unikraft
5+
monitor: qemu
6+
architecture: x86_64
7+
rootfs:
8+
from: scratch
9+
type: initrd
10+
include:
11+
- redis.conf:/conf/redis.conf
12+
kernel:
13+
from: local
14+
path: redis-qemu-x86_64
15+
cmdline: "redis-server /conf/redis.conf"

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ nav:
3535
- Developer Guide:
3636
- developer-guide/*.md
3737
- API Reference: https://pkg.go.dev/github.com/urunc-dev/urunc/pkg/unikontainers
38+
- Examples: examples.md
3839
- Tutorials:
3940
- tutorials/*.md
4041

0 commit comments

Comments
 (0)