Join our Discord Server
Ajeet Raina Ajeet Singh Raina is a former Docker Captain, Community Leader and Arm Ambassador. He is a founder of Collabnix blogging site and has authored more than 570+ blogs on Docker, Kubernetes and Cloud-Native Technology. He runs a community Slack of 8900+ members and discord server close to 2200+ members. You can follow him on Twitter(@ajeetsraina).

A Quick Look at LinuxKit Packaging System

5 min read

One of the most compelling feature of LinuxKit is “Everything replaceable and customisable”. You can now build up your own customised Linux Operating System so as to run it across various platforms like Bare Metal Hardware, Virtual Machines, Cloud Instances etc. As of today, LinuxKit supports various platform like OSX/Hypervisor, Google Cloud Platform,, QEMU/KVM &  VMware ESXi vCenter Server. Effort has already been started for enabling this solution on AWS, Azure, Bluemix etc. For Raspberry Pi aspirants, there is already a feature request raised for enabling LinuxKit on ARM64 platform.

Screen Shot 2017-06-03 at 9.44.56 AM

LinuxKit is built with containers to run the containers. It involves easy tooling and that too with easy iteration.Under LinuxKit, we have runc and containerd which are used to run containers.Containers under LinuxKit are managed by runc and containerd and not by Docker.Everything apart from the init process and runc/containerd run in a container. Under this blog post, I will take you through Packaging System under LinuxKit. This might help you build your own customised OS for your infrastructure requirement.

Screen Shot 2017-06-03 at 9.41.00 AM

Building Packages Under LinuxKit:

Packages under LinuxKit are just container images stored on DockerHub or in private registries. They are stored under Dockerhub under LinuxKit org.

Screen Shot 2017-06-03 at 7.42.13 AM

Base packages for LinuxKit are stored under /pkg directory under LinuxKit repository. To view it, you can clone LinuxKit GITHUB repository and look under linuxkit/pkg directory as shown below:


$git clone

$cd linuxkit/pkg/



Screen Shot 2017-06-03 at 6.56.14 AM


There are different ways for building LinuxKit base packages. You can either use DockerHub images directly or build your own wrapper around DockerHub images in the form of shell script. You have flexibility to use customised base images or custom package with just 1 binary.

It is important to note that Packaging System under LinuxKit uses extensive use of Multi-Stage Builds to Keep package Smaller. To demonstrate, let us pick up one base package – OpenNTPD and see what does it contain.

Screen Shot 2017-06-03 at 9.38.57 AM


Ajeets-MacBook-Air:openntpd ajeetraina$ pwd;ls


Dockerfile Makefile etc


As shown above, OpenNTPD contains a Dockerfile, Makefile & etc directory to hold NTP configurational changes.


Screen Shot 2017-06-03 at 7.54.05 AM

There are multiple FROMs in this Dockerfile which indicates that packages are built using Multi-Stage Build environment. Let us closely look line by line:


FROM linuxkit/alpine:630ee558e4869672fae230c78364e367b8ea67a9



In the first stage, we are trying to pull ALPINE base image which is part of LinuxKit project. LinuxKit has specific versions of all the packages and hence you can see that specific version is hard-coded.


RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/


This creates a directory where it is going to install the  contents of the packages.


RUN apk add –no-cache –initdb -p /out \

    alpine-baselayout \

    busy box \

    musl \

    openntpd \

    && true


It uses Alpine Package Manager(APK) to install standard base layout for standard root filesystem, busy box for SHELL environment, musl in order to have C libraries which is used by all of these components and finally OpenNTP related packages.

At the end of first stage, a container image based on Alpine base image having a directory holding required files and packages with root filesystem enough to run OpenNTP container is successfully built.


RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache


This removes temporary/unwanted files from OUT directly so as to keep the container smaller in size.

Under the 2nd Stage, the content of Dockerfile look like this:

Screen Shot 2017-06-03 at 9.10.29 AM


This starts with empty container image. We copy it from the first stage (i.e.. OUT directory) to our root filesystem. Then OpenNTPD daemon configuration & OpenNTP scripts are added from external /etc directory to the container image. Next, we specify the command that whenever container starts, we need OpenNTP daemon to be started.This provides us a minimal package which runs OpenNTPD service.

The last line is very important and recently been introduced under LinuxKit project. If you remember while building YAML file for LinuxKit, we used to specify CAPABILITIES section. For this OpenNTP service, we won’t require it now because we have now LABEL section added during the build process.  We have bind mounts so that /etc is mounted, network namespace can be specified, CAP_NET_BIND_SERVICE which helps in binding the container  to the interface.

Screen Shot 2017-06-03 at 9.24.45 AM

File: Makefile

Screen Shot 2017-06-03 at 8.43.44 AM

It contains docker build command, squash parameter is passed to squash the layer of the image into a single image to make it smaller in the 2nd stage, –network=none to indicates that while booting the container , it has no access to the network. Other essential entries includes the HASH value provides you with the content hash of all the files in the directory and hence  it becomes important to ensure that only the required files are being used to build up container image.

Let us try to run make command and see if this builds up right container image for us.


Ajeets-MacBook-Air:openntpd ajeetraina$ sudo make


docker build –squash –no-cache –network=none -t linuxkit/openntpd:45deeb05f736162d941c9bf494983f655ab80aa5 .

Sending build context to Docker daemon  5.632kB

Step 1/12 : FROM linuxkit/alpine:630ee558e4869672fae230c78364e367b8ea67a9 AS mirror

 —> 72ff54f67634

Step 2/12 : RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/

 —> Running in 175f2b917c9a

 —> 08b114fc941c

Removing intermediate container 175f2b917c9a

Step 3/12 : RUN apk add –no-cache –initdb -p /out     alpine-baselayout     busybox     musl     openntpd     && true

 —> Running in 3109ff2301fa

(1/7) Installing musl (1.1.16-r9)

(2/7) Installing busybox (1.26.2-r4)


(3/7) Installing alpine-baselayout (3.0.4-r0)

Executing alpine-baselayout-3.0.4-r0.pre-install


(4/7) Installing libressl2.5-libcrypto (2.5.4-r0)

(5/7) Installing libressl2.5-libssl (2.5.4-r0)

(6/7) Installing libressl2.5-libtls (2.5.4-r0)

(7/7) Installing openntpd (6.0_p1-r3)

Executing openntpd-6.0_p1-r3.pre-install

Executing busybox-1.26.2-r4.trigger

OK: 3 MiB in 7 packages

 —> 2336220414ec

Removing intermediate container 3109ff2301fa

Step 4/12 : RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache

 —> Running in c39c2d86c898

 —> 9248b40c1e31

Removing intermediate container c39c2d86c898

Step 5/12 : FROM scratch


Step 6/12 : ENTRYPOINT

 —> Running in a1475ce25417

 —> 89a4eefb0f2e

Removing intermediate container a1475ce25417

Step 7/12 : CMD

 —> Running in 3299eb03002b

 —> e0761d845e86

Removing intermediate container 3299eb03002b

Step 8/12 : WORKDIR /

 —> dc50915b1354

Removing intermediate container 37292d5db9f6

Step 9/12 : COPY –from=mirror /out/ /

 —> 60c358443d49

Removing intermediate container 4c746169a103

Step 10/12 : COPY etc/ /etc/

 —> 049744fe228f

Removing intermediate container f9f21176ca4d

Step 11/12 : CMD /usr/sbin/ntpd -d -s

 —> Running in 94c8a776666d

 —> 56d618a1fa76

Removing intermediate container 94c8a776666d

Step 12/12 : LABEL org.mobyproject.config ‘{“capabilities”: [“CAP_SYS_TIME”, “CAP_SYS_NICE”, “CAP_SYS_CHROOT”, “CAP_SETUID”, “CAP_SETGID”]}’

 —> Running in c1514342c0fb

 —> 46987e8c78bc

Removing intermediate container c1514342c0fb

Successfully built c1dbda899687

Successfully tagged linuxkit/openntpd:45deeb05f736162d941c9bf494983f655ab80aa5

DOCKER_CONTENT_TRUST=1 docker pull linuxkit/openntpd:45deeb05f736162d941c9bf494983f655ab80aa5 || \

DOCKER_CONTENT_TRUST=1 docker push linuxkit/openntpd:45deeb05f736162d941c9bf494983f655ab80aa5

Pull (1 of 1): linuxkit/openntpd:45deeb05f736162d941c9bf494983f655ab80aa5@sha256:26e88ffd48262f4a03ed678d2edee35b807b4d7fe561a3aa6577eef325e317c4

sha256:26e88ffd48262f4a03ed678d2edee35b807b4d7fe561a3aa6577eef325e317c4: Pulling from linuxkit/openntpd

Digest: sha256:26e88ffd48262f4a03ed678d2edee35b807b4d7fe561a3aa6577eef325e317c4

Status: Image is up to date for linuxkit/openntpd@sha256:26e88ffd48262f4a03ed678d2edee35b807b4d7fe561a3aa6577eef325e317c4

Tagging linuxkit/openntpd@sha256:26e88ffd48262f4a03ed678d2edee35b807b4d7fe561a3aa6577eef325e317c4 as linuxkit/openntpd:45deeb05f736162d941c9bf494983f655ab80aa5


Let us verify if it built required image or not:

Screen Shot 2017-06-03 at 9.21.56 AM

The image is just 3.62MB which is quite smaller in size.

Now, you should be able to use this OpenNTPD base image under your YAML file so that moby tool can build up LinuxKit OS image:

Screen Shot 2017-06-03 at 9.29.40 AM

The above YAML content has been picked up from docker.yml file which contains ntpd service container created out of OpenNTPD base image.

In the future blog post, I will showcase how to build your own customised Kernel for LinuxKit.

Did you find this blog helpful?  Feel free to share your experience. Get in touch @ajeetsraina

If you are looking out for contribution/discussion, join me at Docker Community Slack Channel.

Know more about the latest LinuxKit project activities clicking on this link.

Have Queries? Join

Ajeet Raina Ajeet Singh Raina is a former Docker Captain, Community Leader and Arm Ambassador. He is a founder of Collabnix blogging site and has authored more than 570+ blogs on Docker, Kubernetes and Cloud-Native Technology. He runs a community Slack of 8900+ members and discord server close to 2200+ members. You can follow him on Twitter(@ajeetsraina).
Join our Discord Server