Zudoku
Tutorials

Rootfses, Volumes and ROMs

Rootfses, volumes and ROMs (Read-Only Memory) are storage types that share many similarities, but have different use cases and results. Below, this tutorial discusses each one of them and how to use them, and then does a quick comparison between the three.

Storage types

Rootfses

The rootfs, also known as the initrd, is the main component in any instance running on Unikraft Cloud, and any UNIX-based operating system. It's used for configuring its default behaviour and contains all files needed for it to boot. On Unikraft Cloud it's used to also start a default app (without any specific configuration). When booting, the system loads rootfs pages in memory, which incurs a small access time characteristic to RAM read/write operations.

At the same time, because the rootfs resides in volatile memory, changes aren't persistent across reboots. Because of this it's best to put only essential information in there to keep the size down (1-2GB maximum), to save on boot times and on used RAM. For persistent storage, consider using volumes instead.

A rootfs can be of two types: CPIO or EROFS. Each type has its own advantages and disadvantages, but all-around EROFS is better suited for Unikraft Cloud. The rootfs formats documentation page details both types. You can create a rootfs with the CLI for both types, for example:

CPIO

TerminalCode
# make sure to specify the cpio format in Kraftfile # ... # rootfs: # source: ./Dockerfile # format: cpio # ... unikraft build . --output <my-org>/node-playwright-chromium:latest unikraft run --metro fra \ -p 443:8080/http+tls \ -m 2700M \ --image <my-org>/node-playwright-chromium:latest

EROFS

TerminalCode
# make sure to specify the erofs format in Kraftfile # ... # rootfs: # source: ./Dockerfile # format: erofs # ... unikraft build . --output <my-org>/node-playwright-chromium:latest unikraft run --metro fra \ -p 443:8080/http+tls \ -m 900M \ --image <my-org>/node-playwright-chromium:latest

unikraft build keeps the file owners of a Dockerfile, image, or tarball source, the way Docker does, so COPY --chown means what it says. Files from a directory source belong to root. EROFS is a read-only file system, so you can't change owners in place. To change them at run time, have the entrypoint copy those files to a writable path (for example, a volume) and run chown there.

Volumes

Volumes are a persistent, large storage component on Unikraft Cloud. You attach volumes to stopped instances or before you create them through the API. They're automatically mounted in the instance at the specified path either RW (Read-Write) or RO (Read-Only). They're backed up on SSD (Solid State Drive) disks and offer redundancy without compromising on access latency. As such you can use them for persistent data, secrets, storage, logs, and more, and you can share them between instances.

To create and use a volume, you can use the CLI volume commands. For example, you need to first create a volume:

TerminalCode
unikraft volumes create --metro fra \ --name my-volume \ --size 100

Then you can optionally populate it with local data:

TerminalCode
unikraft volume import \ my-volume \ --source my-data/

Finally, when you deploy an instance you can attach the volume to it:

TerminalCode
unikraft run --metro fra \ -m 512M \ -p 443:8080/http+tls \ -v my-volume:/mnt \ --image <my-org>/my-app:latest

ROMs

ROMs are an extension of the rootfs concept and a good way to customize generic rootfses for many instances. They're attached on stopped instances and Unikraft Cloud can mount them automatically in the instance. They're read-only and there is a limit in size, and as such should contain only custom rootfs code/data. They're intended to be small, fast, and flexible as opposed to volumes.

For example, you can use ROMs in the context of a web server that offers different index pages based on the ROM attached for localization. By using ROMs, you can have a single slim image to which you can attach different ROMs depending on the language you want to serve. Finally, it's considered good practice to keep ROMs small and focused, and the instance shouldn't depend on them to boot correctly.

You could package ROMs as an OCI image and push them to the registry, or you could create them directly from a local directory. They show up as any other image in the registry, but don't have a kernel. Because a ROM has no runtime to take architectures from, its Kraftfile must name them under targets, or you pass --arch on the command line. To package them you can use the CLI:

TerminalCode
# make sure to specify the rom path and the build targets in the Kraftfile # ... # targets: # - kraftcloud/x86_64 # roms: # - ./fs # ... unikraft build . --output <my-org>/function-hello:latest

You can then specify ROMs when creating an instance:

unikraft run --metro fra \ --rom name=function-hello,image=<my-org>/function-hello:latest,at=/rom \ ...

Comparison

Type / MetricRootfsesVolumesROMs
I/O SpeedFastDecentFast
I/O LatencySmallNoticeableSmall
Storage MediumRAMSSD/HDDRAM
Intended SizeMedium(up to) LargeSmall
File SystemCPIO/EROFSext4/virtio-fsAny
MountingAutomaticAutomaticAutomatic/Manual
PersistenceNoYesYes (as read-only image)
MandatoryYesNoNo

Conclusion

As expected, each storage type has its own advantages and disadvantages. Rootfses are mandatory and fast, but you should keep them small and only contain essential information. Volumes are persistent and large, but have noticeable access latency. ROMs are a flexible way to customize rootfses, but you should also keep them small and not be essential for booting.

Learn more

Last modified on