Introduction to Unikernel: Building and Deploying Lightweight, Secure Applications

Priyanka Babu

Updated on Jul 15, 2025

Have you ever wondered what it would be like to have everything in the world just for yourself — where every resource and every service is just for you? Imagine you have rented a private villa on a small, quiet island. Everything in the villa — the rooms, the pool, and the beach — is just for you. No other guests can use anything there, and the staff are there just for you. Isn’t that exciting? I’m sure it is! This concept also applies to applications, which are given their own space to work in without interference from others and without sharing resources. Any application could thrive in such an environment. So, the question is: “Does such an application environment exist?” Enter unikernels — a unique application environment that can provide an exclusive environment just like your private villa. They are compact, single-application virtual machines, boosting speed, efficiency, and security. In this article, we will take a closer look at what unikernels are, explore the different types of unikernels, discuss their benefits and limitations, and learn how to create and deploy a simple Nanos application (a type of unikernel) on AWS.

What is a unikernel and why do we need it?

Before we look at unikernels, let us first understand why we need something like unikernels in the first place, and examine the limitations of current operating systems like Linux and Windows that lead us to look for alternatives. Traditional operating systems are designed to run multiple applications simultaneously, which requires careful resource management to ensure smooth operation. The operating system kernel also performs numerous background tasks via memory management, process scheduling, disk I/O and peripheral management. However, our backend applications often do not need these functions, and yet the kernel consumes a lot of memory and CPU power to support them. The solution to the above problem is simple: if the operating system causes too much overhead, why not let the user-space program take control of the hardware directly? This way, the application essentially becomes part of the kernel, accesses the hardware with fewer obstacles and significantly reduces the overhead of the operating system. This is exactly the goal of unikernels — fine-tuning for specific tasks. They are lightweight, specialized virtual machines designed to run a single application, making the system smaller, faster and more secure. This unique form of virtualization merges the application and operating system into a single executable image, eliminating the need for the extensive features and services of traditional operating systems. This specialization makes the system simpler and reduces resource consumption. It only contains the essential components required for the respective application and is, therefore, much smaller than conventional operating systems. This lean design leads to faster boot times and lower memory consumption. In addition, the smaller attack surface of unikernels also improves security. Since it is executed as a single process, there is no overhead from context switching between multiple processes, resulting in faster execution and lower latency. Once compiled, unikernels can be run on local devices on Linux with QEMU and even on various hypervisors such as Xen, KVM, Nitro System and ESXi. These hypervisors handle the interaction with the underlying hardware, allowing unikernels to benefit from virtualization without the overhead of a full operating system. The following block diagram will help you to better understand and visualize the whole thing.

Types of unikernel

Recently, we reached out to several unikernel teams for one of our projects, but only received a few responses. After investigating some of the unikernels, we realized that only two — Nanos and Unikraft — are on the path to stability. Although conceptually both have the same basic principle, they have their own methods for deploying unikernel images and instances. The main differences between the two unikernels are listed in the table below. However, one of the main differences between Unikraft and Nanos is that Unikraft relies more on its own cloud structure called Unikraft Cloud (which is ultimately based on a bare metal instance) for better performance, while Nanos offers the ability to create its own image that can run on all instance types on all cloud providers supported by Nanos.

Building Nanos unikernel (step-by-step instructions)

We have mentioned about Unikraft, but let’s leave that aside for now and concentrate on nanos. Creating a Nanos unikernel involves several steps, from setting up the environment to deploying the final image and instance. In addition, Nanos uses an operational tool called OPS that streamlines various tasks, such as image and instance creation. Before we get into creating images and instances, we first need to build both Nanos and the OPS tool from source code. Alternatively, we could also use pre-built versions of OPS and Nanos to run our application (refer nanovms). However, in this article, we will focus on building Nanos and OPS from source code.

Build OPS and Nanos

Here is a simple step-by-step guide to help you create the OPS and Nanos code from scratch (source: nanovm ):
  1. The very first step is to set up a Linux computer as a host computer for various operational tasks. This can either be a local Linux computer on site or an AWS virtual machine.
Make sure you have the latest version of Go (Golang) installed along with the GCC compiler and make.

sudo apt-get remove golang-go
 sudo apt-get remove --auto-remove golang-go
 sudo apt-get update
 sudo rm -rf /usr/local/go

 # Download the latest go version (above 1.22.1)
 wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz

 # Unzip the downloaded tar file
 sudo tar -C /usr/local -xzf ~//go*.linux-amd64.tar.gz

 # Set the path
 export PATH=$PATH:/usr/local/go/bin

 # Before we install GCC, update the packet list
 sudo apt-get update

 # Install GCC
 sudo apt-get install gcc

 # Verify the GCC Installation
 gcc --version

 # Install make
 sudo apt-get install make
  

sudo apt-get remove golang-go
 sudo apt-get remove --auto-remove golang-go
 sudo apt-get update
 sudo rm -rf /usr/local/go

 # Download the latest go version (above 1.22.1)
 wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz

 # Unzip the downloaded tar file
 sudo tar -C /usr/local -xzf ~//go*.linux-amd64.tar.gz

 # Set the path
 export PATH=$PATH:/usr/local/go/bin

 # Before we install GCC, update the packet list
 sudo apt-get update

 # Install GCC
 sudo apt-get install gcc

 # Verify the GCC Installation
 gcc --version

 # Install make
 sudo apt-get install make
3. Log in to your Linux computer via SSH and make sure that the path to go is included in your Linux computer path.

# Below path should be present in the Linux machine path.
export PATH=$PATH:/usr/local/go/bin
4. Clone the OPS source code to a folder of your choice.

  # Command to clone the source code
git clone https://github.com/nanovms/ops.git
  
5. Navigate to the cloned OPS directory and execute the following commands.

  # Installs dependencies for ops
make deps       

# Generates protobuf files.
make generate    

# Build the ops tool, which generates Ops binary.
make build
  
6. After completing the build, copy the OPS (exe) from the OPS directory to the folder “/home/ubuntu/. ops/bin/ops” (location for OPS in Ubuntu).

  # Copy OPS binary
cp ops ~/.ops/bin/ops 

# Update the path
export PATH=$PATH:/home/ubuntu/.ops/bin
  
7. Please note that to update the OPS code, you need to pull the latest code and run the following command.

  go build
  
8. Clone and build Nanos source code

  # Clone Nanos code.
git clone https://github.com/nanovms/nanos.git    

# Build nanos code.
make
  
9. After successful build, copy the following files into the folder with the latest OPS version (please replace the OPS version (0.1.xx ) with the latest OPS version).

  # Copy the post build files.
cp output/platform/pc/boot/boot.img ~/.ops/0.1.xx/. 
cp output/platform/pc/bin/kernel.img ~/.ops/0.1.xx/.
cp output/tools/bin/mkfs ~/.ops/0.1.xx/.
cp -r output/klib/bin/* ~/.ops/0.1.xx/klibs/.
  
10. Before creating an image and instance on AWS, make sure your ~/.aws/credentials file is set up correctly and create a bucket in S3 storage.

  # Run the below command to configure your AWS credentials.
aws configure

# Enter Access Key ID
AWS Access Key ID:

# Enter Secret Access Key
AWS Secret Access Key:

# Region details
Default region name [ap-south-1]:

# Output format
Default output format [None]:

# Install AWSCLI
sudo apt install awscli

# Run the following command to create a sample bucket on ap-south-1 region.
aws s3api create-bucket --bucket --region ap-south-1
  

Create image and instance

  1. Below is an example of the image configuration file. The detailed configuration of the OPS tool can be found under this link: Ops documentation:
    
      # Image Config file
    
    {
    
        "Args": ["./sample application"],
    
        "CloudConfig" :{
            "Platform" :"aws",
            "ProjectID" :"",
            "Zone":""
            "BucketName":"",
      
    Sample command for creating an image.
    
      ops image create -c  -i  -t aws 
      
    Once the image has been successfully created, it would look like this:
    
      ubuntu@ip-172-22-0-132:~/testing/testing$ ops image create -c instancenew.json -t aws -i unikernel_image file1
    100% |███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| (4098/4098, 399 it/s)
    aws image 'unikernel_image' created...
      
  2. Example of an instance configuration file.
    
      # Instance config file
    
    {
        "CloudConfig" :{
            "Platform" :"aws",
            "ProjectID" :"",
            "flavor":"",
            "Zone":"",
            "VPC": "",
            "Subnet":""
    
        },
    
        "RunConfig": {
            "Ports":["80","443","8080"]
         }
    
    }
      
      Sample command for creating an instance.
    
      ops instance create -c  -i   -t aws
      
    A successful instance creation would look like this:
    
      ubuntu@ip-172-22-0-132:~/testing/testing$ ops instance create -i unikernel_instance Unikernel_image -t aws -c instancenew.json
     Created security group sg-0f6b9bc85c2df01e3 with VPC vpc-3a45dc53.
     aws instance 'unikernel_instance' created...
      
    Here you can see an example of a successfully deployed and running nanos application on AWS (Running instance).
  3. Alternatively, we can use QEMU to run the Nanos image on our local Linux.
left-icon
1

of

4
right-icon

India’s choice for business brilliance

Work faster, manage better, and stay on top of your business with TallyPrime, your complete business management solution.

Get 7-days FREE Trial!

I have read and accepted the T&C
Submit