Accessing the host filesystem from a Nanos guest using 9P

A Nanos unikernel image contains three main components:

  • the bootloader
  • the kernel binary file
  • the root partition
The root partition contains, beside the executable file that is run upon instance startup, the files and folders that your unikernel application can access, and is the backing storage for any new files and folders that your application may create when running. This partition is formatted using the Tuple Filesystem (TFS). Thus, creating a Nanos image involves taking a set of files and folders in your development machine and converting them into a TFS filesystem, which is then witten to the root partition of the image; this is what our orchestration tool Ops does behind the scenes when you run commands such as ops run or ops pkg load.

After running a Nanos instance, thanks to native TFS support in Ops you can see the contents of the filesystem used by the instance (including any changes made by the instance when running) by using commands such as ops image ls and ops image tree, and you can copy files from the image (including files created and/or modified by the instance) to your local filesystem with ops image cp. With our FUSE driver, you can even mount the root partition of an image on your computer, so you can read and write files in the image as you would read and write any other files in your computer. And after unmounting the image, you can restart the Nanos instance, and any modifications you made to its root filesystem are visible to the newly started instance.

These are all useful tools that can help you speed up your development cycle when creating a unikernel image and testing changes, but they require the instance to be stopped when you make any modifications to its filesystem (by either re-creating the image from scratch using Ops, or modifying its filesystem using the FUSE driver). With the addition of VirtFS support to the Nanos kernel, you can now create/modify/delete files in your computer and see these changes in your running instance without rebooting it, thus speeding up your development cycle even further.

Running a unikernel with access to the host filesystem

9P is a network protocol developed for the Plan 9 operating system from Bell Labs, that allows the operating system to use a remote filesystem, i.e. access files and folders located in another computer. VirtFS is a filesystem implementation where a VirtIO device communicates with the operating system via the 9P protocol, allowing a virtual machine (guest) to access files and folders located on the host machine; VirtFS is implemented in QEMU, which is the hypervisor used by Ops, thus by having VirtFS support in the Nanos kernel we are able to run unikernel applications that can access the host filesystem.

The simplest way to export a folder in the host filesystem to a Nanos instance is by specifying a --mounts <host_folder>:<guest_folder> option in the ops run or ops pkg load command: the <host_folder> part indicates what folder on the host must be exported by the hypervisor, while <guest_folder> is where the host folder will be mounted in the guest filesystem, i.e. where the folder contents will be visible to your application running as a unikernel. Alternatively to using the --mounts command line option, it is possible to specify mount directives as JSON attributes in the Ops configuration file, as in the following example:

{
  "Mounts": {
    "host_folder": "/guest_folder"
  }
}

A common use case where this feature can be really useful is when your unikernel program is a web server and you want to live-edit its contents: by configuring the program to serve files from a filesystem path where a host folder is mounted, you can modify the contents of the folder on your computer, and these changes will be immediately visible when you make a new request to the server. Below is the code of a very simple web server written in Go:

package main

import (
	"net/http"
)

func main() {
	fs := http.FileServer(http.Dir("static/"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	http.ListenAndServe(":8080", nil)
}

This server listens on port 8080 and serves the contents of the /static folder. When running this program as a Nanos unikernel, if you specify a mount directive where a folder on your computer is mounted at the /static path in the instance, you can see the contents of that folder in your browser whenever you make a web request to http://127.0.0.1:8080/static/. To try it out, run the web server with the command below (where my_server should be replaced with the name of your executable file, and my_dir should be replaced with the path of the folder on your computer that you want to serve):

ops run my_server --mounts my_dir:/static -p 8080

If you then go to http://127.0.0.1:8080/static/ you will see the up-to date contents of the folder on your computer. Try adding/modifying/deleting files, then refresh the page on your browser and see how these changes are immediately reflected in the web page, whether it's listing a directory or showing the contents of a file.

Deploy Your First Open Source Unikernel In Seconds

Get Started Now.