tiny-linux

Running Linux in your browser

27 Feb 2026

Mauri de Souza Meneguzzo

Emulate all the things!

Bare minimal Linux Kernel + Busybox userspace, emulated with v86

linux.mauri870.com

Three static files served over HTTP: bzImage, init.cpio.lzma, index.html.

2

v86: an x86 PC in WebAssembly

v86 emulates an x86 PC entirely in WebAssembly.

It emulates:

The emulator speaks to the user via xterm.js, a full terminal emulator in the browser.

3

Building the kernel

The kernel is compiled from source with Clang/LLVM:

cp linux.config linux/.config
cd linux && make LLVM=1 -j $(nproc)
cp arch/x86/boot/bzImage ../build/bzImage

A minimal linux.config, no modules, no drivers for hardware that doesn't exist in v86, just enough to boot and mount an initramfs:

Compressed kernel image comes out around 3 MB.

4

BusyBox: a whole userspace in one binary

BusyBox bundles sh, ls, cat, grep, vi, top, ps, getty, init, and hundreds more into a single statically-linked executable.

cp busybox.config busybox/.config
cd busybox && make CC=clang -j $(nproc)

Each applet is just a symlink to the busybox binary:

cd build/initramfs && ln -f busybox sh

Configuration selects only the tools that fit in ~2 MB. The final binary is stripped.

5

Building the initramfs

An initramfs is the root filesystem the kernel mounts before pivoting to the real root. For tiny-linux, it is the real root, there is no disk.

find . | cpio -H newc -o | lzma > ../init.cpio.lzma

The rootfs layout:

/bin/busybox    # the multi-call binary
/bin/sh         # symlink → busybox
/etc/inittab    # getty on ttyS0
/etc/passwd
/proc /sys /dev /tmp
6

init and the boot sequence

The kernel hands control to /init after mounting the initramfs.

The emulator boots the kernel, which writes to ttyS0, which becomes xterm.js output. Keypresses go from xterm.js → v86 serial port → kernel UART driver → getty → sh.

7

Testing with QEMU

Before shipping to the browser, you can test the image locally:

qemu-system-x86_64 \
    -kernel build/bzImage \
    -initrd build/init.cpio.lzma

QEMU and v86 both load the same bzImage + initramfs. If it boots in QEMU, it boots in the browser.

This cuts the iteration cycle, no need to rebuild the browser bundle every time.

8

Serving it

The final build output is three files:

build/
  bzImage         # compressed kernel (~955 KB)
  init.cpio.lzma  # initramfs (1.1 MB)
  index.html      # the browser glue, v86 wasm, xterm.js

cd build && npx http-server

Point a browser at localhost:8080. The kernel boots in about two seconds.

9

Useful links

Live Demo linux.mauri870.com

GitHub Repo github.com/mauri870/tiny-linux

Linux Kernel

V86 x86 cpu emulator

BusyBox Userspace

My talks are written with golang.org/x/tools/present

Find this talk at talks.mauri870.com

10

Thank you

27 Feb 2026

Mauri de Souza Meneguzzo

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)