Skip to content

Commit 170b580

Browse files
committed
feat: add .NET Kestrel HTTP server example
ASP.NET Core Kestrel web server on Hyperlight using the same async networking infrastructure as go-http. Uses CreateEmptyBuilder to avoid inotify dependency (ENOSYS on Unikraft). Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent d0989da commit 170b580

5 files changed

Lines changed: 195 additions & 0 deletions

File tree

examples/dotnet-http/Dockerfile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
2+
WORKDIR /app
3+
COPY Program.cs KestrelHyperlight.csproj ./
4+
RUN dotnet publish -c Release -r linux-musl-x64 --self-contained \
5+
-p:PublishTrimmed=true \
6+
-p:InvariantGlobalization=true \
7+
-o /out
8+
9+
FROM scratch AS rootfs
10+
11+
# .NET app + runtime (self-contained)
12+
COPY --from=build /out/ /app/
13+
14+
# musl dynamic linker
15+
COPY --from=build /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1
16+
17+
# C++ runtime libraries (needed by .NET runtime)
18+
COPY --from=build /usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so.6
19+
COPY --from=build /usr/lib/libgcc_s.so.1 /usr/lib/libgcc_s.so.1
20+
21+
# --- CPIO rootfs builder (used by: docker build --target cpio) ---
22+
FROM alpine:3.20 AS cpio
23+
RUN apk add --no-cache cpio findutils
24+
COPY --from=rootfs / /rootfs/
25+
RUN cd /rootfs && find . | cpio -o -H newc > /output.cpio 2>/dev/null

examples/dotnet-http/Justfile

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# dotnet-http (Kestrel) on Hyperlight
2+
#
3+
# just run - Run the HTTP server (--port implies --net)
4+
# just rootfs - Build rootfs via Docker (cross-platform)
5+
# just build - Build/pull kernel
6+
# just clean - Remove build artifacts
7+
8+
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
9+
export DOCKER_BUILDKIT := "0"
10+
11+
kernel := ".unikraft/build/dotnet-http-hyperlight_hyperlight-x86_64"
12+
initrd := "initrd.cpio"
13+
memory := "128Mi"
14+
image := "dotnet-http-hyperlight"
15+
16+
# Run the HTTP server (curl http://localhost:8080 to test)
17+
run:
18+
hyperlight-unikraft {{kernel}} --initrd {{initrd}} --memory {{memory}} --port 8080 -- /app/KestrelHyperlight
19+
20+
# Build rootfs via Docker (cross-platform)
21+
rootfs:
22+
docker build --platform linux/amd64 --target cpio -t {{image}}-cpio .
23+
- docker rm -f {{image}}-tmp
24+
docker create --name {{image}}-tmp {{image}}-cpio /bin/true
25+
docker cp {{image}}-tmp:/output.cpio ./{{initrd}}
26+
docker rm -f {{image}}-tmp
27+
28+
# Build kernel via kraft (Linux)
29+
[unix]
30+
build:
31+
-kraft-hyperlight build --plat hyperlight --arch x86_64
32+
33+
# Pull pre-built kernel from GHCR (Windows — no kernel published yet)
34+
[windows]
35+
build:
36+
Write-Host "No pre-built kernel for dotnet-http yet. Build on Linux first."
37+
38+
# Clean + rebuild everything
39+
rebuild: clean build rootfs
40+
41+
# Clean build artifacts
42+
[unix]
43+
clean:
44+
rm -rf .unikraft {{initrd}}
45+
46+
[windows]
47+
clean:
48+
if (Test-Path .unikraft) { Remove-Item -Recurse -Force .unikraft }
49+
if (Test-Path {{initrd}}) { Remove-Item -Force {{initrd}} }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>net9.0</TargetFramework>
4+
<ImplicitUsings>enable</ImplicitUsings>
5+
<InvariantGlobalization>true</InvariantGlobalization>
6+
</PropertyGroup>
7+
</Project>

examples/dotnet-http/Program.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions());
2+
builder.WebHost.UseKestrelCore();
3+
4+
var app = builder.Build();
5+
app.Urls.Add("http://0.0.0.0:8080");
6+
7+
app.Run(async context =>
8+
{
9+
await context.Response.WriteAsync("Hello from Kestrel on Hyperlight!");
10+
});
11+
12+
Console.WriteLine("Listening on :8080");
13+
await app.RunAsync();

examples/dotnet-http/kraft.yaml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
specification: '0.6'
2+
name: dotnet-http-hyperlight
3+
4+
unikraft:
5+
source: https://github.com/unikraft/unikraft.git
6+
version: plat-hyperlight
7+
kconfig:
8+
# Platform
9+
CONFIG_PLAT_HYPERLIGHT: 'y'
10+
CONFIG_PAGING: 'n'
11+
CONFIG_LIBUKVMEM: 'n'
12+
CONFIG_LIBUKINTCTLR_HYPERLIGHT: 'y'
13+
CONFIG_HYPERLIGHT_MAX_GUEST_LOG_LEVEL: 0
14+
15+
# Suppress kernel logging for performance
16+
CONFIG_LIBUKPRINT_KLVL_CRIT: 'y'
17+
CONFIG_LIBUKPRINT_PRINT_TIME: 'n'
18+
CONFIG_LIBUKPRINT_PRINT_SRCNAME: 'n'
19+
CONFIG_LIBUKBOOT_BANNER_NONE: 'y'
20+
21+
# Size optimization
22+
CONFIG_OPTIMIZE_SIZE: 'y'
23+
CONFIG_OPTIMIZE_PIE: 'y'
24+
25+
# VFS and initrd support
26+
CONFIG_LIBVFSCORE: 'y'
27+
CONFIG_LIBVFSCORE_AUTOMOUNT_CI: 'y'
28+
CONFIG_LIBVFSCORE_AUTOMOUNT_CI_CUSTOM: 'y'
29+
CONFIG_LIBVFSCORE_AUTOMOUNT_CI0_MP: '/'
30+
CONFIG_LIBVFSCORE_AUTOMOUNT_CI0_DRIVER: 'cpiovfs'
31+
CONFIG_LIBVFSCORE_AUTOMOUNT_CI0_UKOPTS_IFINITRD0: 'y'
32+
CONFIG_LIBCPIOVFS: 'y'
33+
CONFIG_LIBUKCPIO: 'y'
34+
35+
# ELF loader - .NET apphost binary
36+
CONFIG_APPELFLOADER: 'y'
37+
CONFIG_APPELFLOADER_VFSEXEC: 'y'
38+
CONFIG_APPELFLOADER_CUSTOMAPPNAME: 'n'
39+
CONFIG_APPELFLOADER_VFSEXEC_ENVPATH: 'n'
40+
CONFIG_APPELFLOADER_VFSEXEC_PATH: '/app/KestrelHyperlight'
41+
CONFIG_APPELFLOADER_VFSEXEC_EXECBIT_CHECK: 'n'
42+
CONFIG_LIBPOSIX_ENVIRON_ENVP0: '"PATH=/app:/usr/bin:/bin"'
43+
CONFIG_LIBPOSIX_ENVIRON_ENVP1: '"DOTNET_GCHeapHardLimit=0x8000000"'
44+
CONFIG_LIBPOSIX_ENVIRON_ENVP2: '"DOTNET_gcServer=0"'
45+
CONFIG_LIBPOSIX_ENVIRON_ENVP3: '"DOTNET_EnableWriteXorExecute=0"'
46+
CONFIG_LIBPOSIX_ENVIRON_ENVP4: '"COMPlus_EnableDiagnostics=0"'
47+
CONFIG_LIBPOSIX_ENVIRON_ENVP5: '"DOTNET_DefaultStackSize=0x10000"'
48+
CONFIG_LIBPOSIX_ENVIRON_ENVP6: '"hostBuilder__reloadConfigOnChange=false"'
49+
CONFIG_LIBELF: 'y'
50+
51+
# Random number support
52+
CONFIG_LIBUKRANDOM_CMDLINE_SEED: 'y'
53+
CONFIG_LIBUKRANDOM_GETRANDOM: 'y'
54+
55+
# Stack size (order 9 = 2^9 pages = 2MB, needed for CoreCLR's 1.5MB alloca)
56+
CONFIG_STACK_SIZE_PAGE_ORDER: 9
57+
58+
# Threading support (required for .NET runtime)
59+
CONFIG_LIBUKBOOT_MAINTHREAD: 'y'
60+
CONFIG_LIBPOSIX_PROCESS_ARCH_PRCTL: 'y'
61+
CONFIG_LIBPOSIX_PROCESS_MULTITHREADING: 'y'
62+
CONFIG_LIBPOSIX_PROCESS_SIGNAL: 'y'
63+
CONFIG_LIBPOSIX_FUTEX: 'y'
64+
CONFIG_LIBUKSCHED: 'y'
65+
CONFIG_LIBUKSCHEDCOOP: 'y'
66+
CONFIG_LIBUKMPI: 'y'
67+
CONFIG_LIBUKLOCK: 'y'
68+
CONFIG_LIBUKLOCK_SEMAPHORE: 'y'
69+
CONFIG_LIBUKLOCK_MUTEX: 'y'
70+
71+
# mmap needed by CoreCLR memory management
72+
CONFIG_LIBUKMMAP: 'y'
73+
74+
# Event/polling support
75+
CONFIG_LIBPOSIX_POLL: 'y'
76+
CONFIG_LIBPOSIX_EVENTFD: 'y'
77+
CONFIG_LIBPOSIX_FDIO: 'y'
78+
CONFIG_LIBPOSIX_FDTAB: 'y'
79+
CONFIG_LIBUKFILE: 'y'
80+
81+
# devfs + /dev/hcall (required for host-proxied networking)
82+
CONFIG_LIBDEVFS: 'y'
83+
CONFIG_LIBDEVFS_AUTOMOUNT: 'y'
84+
CONFIG_HYPERLIGHT_HCALL: 'y'
85+
86+
# Networking: host-proxied sockets
87+
CONFIG_LIBPOSIX_SOCKET: 'y'
88+
CONFIG_LIBHOSTSOCK: 'y'
89+
90+
91+
libraries:
92+
app-elfloader:
93+
source: https://github.com/unikraft/app-elfloader.git
94+
version: plat-hyperlight
95+
libelf:
96+
source: https://github.com/unikraft/lib-libelf.git
97+
version: staging
98+
99+
targets:
100+
- architecture: x86_64
101+
platform: hyperlight

0 commit comments

Comments
 (0)