From 1208aaaefd03fef63a7470d5b411d03c7c25078f Mon Sep 17 00:00:00 2001 From: Charalampos Mainas Date: Fri, 23 Jan 2026 07:50:16 +0000 Subject: [PATCH 1/2] feat(freebsd): Initial support for freebsd guests Add initial support for FreeBSD guests. In the current status, urunc can boot FreeBSD based workloads which use a block based rootfs. This can be done either through a block image inside the container's image or through devmapper and the use of ext2 as the filesystem for the block-based snapshot. Since FreeBSD does not accept arbitrary kernel boot parameters, nor accepts network configuration through kernel boot parameters, the current implementation attaches an extra text file through virtio block. This text file contains the urunit configuration which has been updates to also include the network configuration and the application, along with its arguments. Furthermore, the current approach is limited to Firecracker, since the Qemu microVM support does not seem to work properly for FreeBSD. Signed-off-by: Charalampos Mainas --- .github/linters/urunc-dict.txt | 7 +- pkg/unikontainers/unikernels/freebsd.go | 242 ++++++++++++++++++++++ pkg/unikontainers/unikernels/unikernel.go | 3 + 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 pkg/unikontainers/unikernels/freebsd.go diff --git a/.github/linters/urunc-dict.txt b/.github/linters/urunc-dict.txt index 538649e0..f4a3b003 100644 --- a/.github/linters/urunc-dict.txt +++ b/.github/linters/urunc-dict.txt @@ -405,4 +405,9 @@ Logr onsi ESRCH Prafful -praffq \ No newline at end of file +praffq +mountfrom +vtbd +acpi +mmio +microvm diff --git a/pkg/unikontainers/unikernels/freebsd.go b/pkg/unikontainers/unikernels/freebsd.go new file mode 100644 index 00000000..82f80174 --- /dev/null +++ b/pkg/unikontainers/unikernels/freebsd.go @@ -0,0 +1,242 @@ +// Copyright (c) 2023-2026, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unikernels + +import ( + "fmt" + "path/filepath" + "strconv" + "strings" + + "github.com/urunc-dev/urunc/pkg/unikontainers/types" +) + +const ( + FreeBSDUnikernel string = "freebsd" + netStartMarker string = "UNS" // Net configuration start marker + netEndMarker string = "UNE" // Net configuration end marker + paddingMarker string = "PAD" // Padding bytes +) + +type FreeBSD struct { + Command []string + Monitor string + Env []string + BlockImgAsRootfs bool + Net FreeBSDNet + Block []types.BlockDevParams + ProcConfig types.ProcessConfig +} + +type FreeBSDNet struct { + Address string + Gateway string + Mask string +} + +func (f *FreeBSD) CommandString() (string, error) { + if f.BlockImgAsRootfs { + return "vfs.root.mountfrom=ext2fs:/dev/vtbd0", nil + } + return "vfs.root.mountfrom=/dev/vtbd0", nil +} + +func (f *FreeBSD) SupportsBlock() bool { + return true +} + +func (f *FreeBSD) SupportsFS(fsType string) bool { + switch fsType { + case "ext2": + return true + default: + return false + } +} + +func (f *FreeBSD) MonitorNetCli(_ string, _ string) string { + // TODO: Commenting out, since FreeBSd does not work properly over Qemu microVM + // switch f.Monitor { + // TODO: Commenting out, since it is not working properly + // case "qemu": + // netOption := " -netdev tap,id=net0,script=no,downscript=no,ifname=" + ifName + // netOption += " -device virtio-net-device,netdev=net0,mac=" + mac + // return netOption + // default: + // return "" + // } + return "" +} + +func (f *FreeBSD) MonitorBlockCli() []types.MonitorBlockArgs { + if len(f.Block) == 0 { + return nil + } + blkArgs := make([]types.MonitorBlockArgs, 0, len(f.Block)) + switch f.Monitor { + // TODO: Commenting out, since it is not working properly + // case "qemu": + // for _, aBlock := range f.Block { + // bcli1 := fmt.Sprintf(" -device virtio-blk-device,serial=%s,drive=%s", aBlock.ID, aBlock.ID) + // bcli2 := fmt.Sprintf(" -drive format=raw,if=none,id=%s,file=%s", aBlock.ID, aBlock.Source) + // blkArgs = append(blkArgs, types.MonitorBlockArgs{ + // ExactArgs: bcli1 + bcli2, + // }) + // } + case "firecracker": + for _, aBlock := range f.Block { + blkArgs = append(blkArgs, types.MonitorBlockArgs{ + ID: "FC" + aBlock.ID, + Path: aBlock.Source, + }) + } + blkArgs = append(blkArgs, types.MonitorBlockArgs{ + ID: "FC_URUNIT_CONFIG", + Path: urunitConfPath, + }) + default: + return nil + } + + return blkArgs +} + +func (f *FreeBSD) MonitorCli() types.MonitorCliArgs { + // TODO: Commenting out, since FreeBSd does not work properly over Qemu microVM + // switch f.Monitor { + // case "qemu": + // monArgs := " -M microvm,rtc=on,acpi=off,pic=off,accel=kvm -global virtio-mmio.force-legacy=false -no-reboot -display none -nodefaults -serial stdio" + // return types.MonitorCliArgs{ + // OtherArgs: monArgs, + // } + // default: + // return types.MonitorCliArgs{} + // } + return types.MonitorCliArgs{} +} + +func (f *FreeBSD) Init(data types.UnikernelParams) error { + // if Mask is empty, there is no network support + if data.Net.Mask != "" { + f.Net.Address = data.Net.IP + f.Net.Gateway = data.Net.Gateway + f.Net.Mask = data.Net.Mask + } + f.Block = data.Block + f.Env = data.EnvVars + f.Command = data.CmdLine + f.Monitor = data.Monitor + f.ProcConfig = data.ProcConf + f.BlockImgAsRootfs = false + if data.Rootfs.MountedPath != "" { + f.BlockImgAsRootfs = true + } + + err := f.setupUrunitConfig(data.Rootfs) + if err != nil { + return err + } + + return nil +} + +// setupUrunitConfig creates the urunit configuration file with environment variables. +func (f *FreeBSD) setupUrunitConfig(rfs types.RootfsParams) error { + urunitConfig := f.buildUrunitConfig() + + urunitConfigFile := filepath.Join(rfs.MonRootfs, urunitConfPath) + err := createFile(urunitConfigFile, urunitConfig) + if err != nil { + return fmt.Errorf("failed to setup urunit config: %w", err) + } + + return nil +} + +// buildEnvConfig creates the environment configuration content for urunit. +func (f *FreeBSD) buildUrunitConfig() string { + // Format: UES\n\n\n...\nUEE\n + var sb strings.Builder + sb.WriteString(envStartMarker) + sb.WriteString("\n") + if len(f.Env) > 0 { + sb.WriteString(strings.Join(f.Env, "\n")) + sb.WriteString("\n") + } + sb.WriteString(envEndMarker) + sb.WriteString("\n") + sb.WriteString(lpcStartMarker) + sb.WriteString("\n") + sb.WriteString("UID:") + sb.WriteString(strconv.FormatUint(uint64(f.ProcConfig.UID), 10)) + sb.WriteString("\n") + sb.WriteString("GID:") + sb.WriteString(strconv.FormatUint(uint64(f.ProcConfig.GID), 10)) + sb.WriteString("\n") + sb.WriteString("WD:") + sb.WriteString(f.ProcConfig.WorkDir) + sb.WriteString("\n") + sb.WriteString("ARC:") + sb.WriteString(strconv.FormatUint(uint64(len(f.Command)), 10)) + sb.WriteString("\n") + for _, c := range f.Command { + sb.WriteString("ARV:") + sb.WriteString(c) + sb.WriteString("\n") + } + sb.WriteString(lpcEndMarker) + sb.WriteString("\n") + sb.WriteString(blkStartMarker) + sb.WriteString("\n") + for _, b := range f.Block { + if b.ID == "rootfs" { + continue + } + sb.WriteString("ID:") + if f.Monitor == "firecracker" { + sb.WriteString("FC") + } + sb.WriteString(b.ID) + sb.WriteString("\n") + sb.WriteString("MP:") + sb.WriteString(b.MountPoint) + sb.WriteString("\n") + } + sb.WriteString(blkEndMarker) + sb.WriteString("\n") + sb.WriteString(netStartMarker) + sb.WriteString("\n") + sb.WriteString("IP:") + sb.WriteString(f.Net.Address) + sb.WriteString("\n") + sb.WriteString("GW:") + sb.WriteString(f.Net.Gateway) + sb.WriteString("\n") + sb.WriteString("MSK:") + sb.WriteString(f.Net.Mask) + sb.WriteString("\n") + sb.WriteString(netEndMarker) + sb.WriteString("\n") + for i := 0; i < 128; i++ { + sb.WriteString(paddingMarker) + sb.WriteString("\n") + } + return sb.String() +} + +func newFreeBSD() *FreeBSD { + freebsdStruct := new(FreeBSD) + return freebsdStruct +} diff --git a/pkg/unikontainers/unikernels/unikernel.go b/pkg/unikontainers/unikernels/unikernel.go index 38326874..700211a5 100644 --- a/pkg/unikontainers/unikernels/unikernel.go +++ b/pkg/unikontainers/unikernels/unikernel.go @@ -39,6 +39,9 @@ func New(unikernelType string) (types.Unikernel, error) { case LinuxUnikernel: unikernel := newLinux() return unikernel, nil + case FreeBSDUnikernel: + unikernel := newFreeBSD() + return unikernel, nil default: return nil, ErrNotSupportedUnikernel } From a531eec6aed3cdd9405dca89f4bf37785a58b2cd Mon Sep 17 00:00:00 2001 From: Charalampos Mainas Date: Fri, 23 Jan 2026 16:44:00 +0000 Subject: [PATCH 2/2] feat(freebsd): Update docs with the newly FreeBSD support Add information in docs for FreeBSD as a supported guest, providing a few information about it and the support over urunc. Signed-off-by: Charalampos Mainas --- README.md | 1 + docs/hypervisor-support.md | 1 + docs/index.md | 1 + docs/unikernel-support.md | 56 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/README.md b/README.md index 3715bb88..6bbf9956 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ supported VM/Sandbox monitors and unikernels: | MirageOS | QEMU, Solo5-hvt, Solo5-spt | x86,aarch64 | Block/Devmapper | | Mewz | QEMU | x86 | In-memory | | Linux | QEMU, Firecracker | x86 | Initrd, Block/Devmapper, 9pfs, Virtiofs | +| FreeBSD | Firecracker | x86 | Block/Devmapper | We plan to add support for more unikernel frameworks and other platforms too. Feel free to [contact](#Contact) us for a specific unikernel framework or similar diff --git a/docs/hypervisor-support.md b/docs/hypervisor-support.md index df349d72..29aecc65 100644 --- a/docs/hypervisor-support.md +++ b/docs/hypervisor-support.md @@ -133,6 +133,7 @@ Supported unikernel frameworks with `urunc`: - [Unikraft](../unikernel-support#unikraft) - [Linux](../unikernel-support#linux) +- [FreeBSD](../unikernel-support#freebsd) An example unikernel: diff --git a/docs/index.md b/docs/index.md index c9fc2e0a..2d3945ae 100644 --- a/docs/index.md +++ b/docs/index.md @@ -60,6 +60,7 @@ Sandbox monitors, along with the unikernels that can run on top of them. | [MirageOS](./unikernel-support#mirage)| [Qemu](./hypervisor-support#qemu), [Solo5-hvt](./hypervisor-support#solo5-hvt), [Solo5-spt](./hypervisor-support#solo5-spt) | x86, aarch64 | Block/Devmapper | | [Mewz](./unikernel-support#mewz)| [Qemu](./hypervisor-support#qemu) | x86 | In-memory | | [Linux](./unikernel-support#linux)| [Qemu](./hypervisor-support#qemu), [Firecracker](./hypervisor-support#aws-firecracker) | x86, aarch64 | Initrd, Block/Devmapper, 9pfs, Virtiofs | +| [FreeBSD](./unikernel-support#freebsd)| [Firecracker](./hypervisor-support#aws-firecracker) | x86 | Block/Devmapper | diff --git a/docs/unikernel-support.md b/docs/unikernel-support.md index f0a8bd8d..92ff6a9b 100644 --- a/docs/unikernel-support.md +++ b/docs/unikernel-support.md @@ -342,6 +342,62 @@ An example of a Redis alpine image transformed to a block file on top of sudo nerdctl run --rm -ti --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/redis-firecracker-linux-block:latest ``` +## FreeBSD + +[FreeBSD](https://www.freebsd.org/) is a popular BSD operating system which +powers modern servers, storage systems, network appliances and embedded +platforms. [FreeBSD](https://www.freebsd.org/) is well known for its +performance, advanced networking, and strong focus on correctness and security. +As a result, many applications and services, especially those requiring +stability and fine-grained system control, are built to run on +[FreeBSD](https://www.freebsd.org/). Of course, +[FreeBSD](https://www.freebsd.org/) is not a unikernel framework. However, +thanks to its its modular architecture, combined with features like custom +kernel configuration, allows us to build highly specialized and minimal system +images tailored for specific workloads. Therefore, it fits well in the single +application model of `urunc`. + +Furtermore, the introduction of [FreeBSD](https://www.freebsd.org/) in the [OCI +spec](https://github.com/opencontainers/runtime-spec) as a target platform +resulted to the creation and distribution of FreeBSD based OCI images. +[FreeBSD](https://www.freebsd.org/) has wide support for different +Therfore, these images can be easily executed on top of `urunc` with a focus on +single application containers. + +### VMMs and other sandbox monitors + +While [FreeBSD](https://www.freebsd.org/) has a wide support for various +hypervisors, it assumes a full VM and not a microVM> However, on 2022 support +for [Firecracker](https://github.com/firecracker-microvm/firecracker) has been +added. On the other hand, the Qemu microVM does not seem to work properly with +[FreeBSD](https://www.freebsd.org/). + +### FreeBSD and `urunc` + +Focusing on the single-application notion of using the +[FreeBSD](https://www.freebsd.org/) kernel, `urunc` provides support for +[Firecracker](https://github.com/firecracker-microvm/firecracker). For network, +`urunc` will make use of virtio-net through MMIO. In the case of storage, +`urunc` can use only virtio-block, since the support for initrd is not there +for [FreeBSD](https://www.freebsd.org/) and shared filesystems are not +supported in +[Firecracker](https://github.com/firecracker-microvm/firecracker). + +As a result, the [FreeBSD](https://www.freebsd.org/) based images for `urunc` +need to either contain a block-based image for rootfs, or use the devmapper +snapshotter with "ext2" as a filesystem, since +[FreeBSD](https://www.freebsd.org/) does not support other Linux-based +filesystems. For more information on setting up devmapper, please take a look +on our [installation guide](../installation#setup-thinpool-devmapper). + +An example of a Caddy HTTP server on top of [FreeBSD](https://www.freebsd.org/) +and [Firecracker](https://github.com/firecracker-microvm/firecracker). with +'urunc' and devmapper as a snapshotter: + +```bash +sudo nerdctl run --rm -ti --snapshotter devmapper --runtime io.containerd.urunc.v2 harbor.nbfc.io/nubificus/urunc/caddy-firecracker-freebsd-raw:latest +``` + ## Future unikernels and frameworks: In the near future, we plan to add support for the following frameworks: