Docker Inside Out – A Journey to the Running Container


Necessity is the mother of invention, same happens here in case of docker. With the pressure of splitting monolithic applications for the purpose of ease, we arrived at docker and it made our life much simpler. We all access docker with docker-cli command but I wonder what it does behind the scene, to run a container. Let’s get deeper into it in this very blog.

There’s a saying that “Behind every successful man, there is a woman”. I would love to give my perspective on this. One of the things that I have actively observed from the life of successful people I know is that there is a lot of truth in this statement but it varies with different situations and in most of the cases these women are not directly helping men in their prime work but taking care of other important work around so that they can concentrate on their prime work. Keeping this in my mind, I am expecting that there are other components as well which are behind docker-cli command that leads to the successful creation of containers. Whenever I talk about docker containers with developers who are new to docker in my organization the only thing I hear from them is “docker-cli command is used to invoke docker daemon to run container”

But, Docker daemon is not the process that gets executed when a container is meant to be run – it delegates the action to containerd which then controls a list of runtimes (runc by default) which is then responsible for creating a new process (calling the defined runtime as specified in the configuration parameters) with some isolation and only then executing the entrypoint of that container.

Components involved

  • Docker-cli
  • Dockerd
  • Containerd
  • RunC
  • Containerd-shim

Docker-cli: Used to make Docker API calls.

Dockerd:  dockerd listens for Docker API requests, dockerd can listen for Docker Engine API requests via three different types of Socket: unix, tcp, and fd and manages host’s container life-cycles with the help of containerd. Hence, actual container life-cycle management is outsourced to containerd. 

Containerd: Actually manages container life-cycle through the below mentioned tasks:

  • Image push and pull
  • Management of storage
  • Of course executing containers by calling runc with the right parameters to run containers.

Let’s go through some subsystems of containerd :

Runc: Containerd uses RunC to run containers according to the OCI specification.

Containerd shim: With docker 1.11 version, this component has been added. This is the parent process of every container started and it also allows daemon-less containers. First it allows the runtimes, i.e. runc, to exit after which it starts the container.  This way we don’t have to have the long running processes for containers.  When you start nginx you should only see the nginx process and the shim.  

Daemon-less: When I say daemon-less containers in the above paragraph, it means there is an advantage of this. When containerd shim was not there, upgrading docker daemon without restarting all your containers was a big pain. Hence, containerd shim got introduced to solve this problem.

The communication between Dockerd and ContainerD

We can see how docker delegates all the work of setting up the container to containerd. Regarding interactions between docker, containerd, and runc, we can understand that without even looking at the source code – plain strace and pstree can do the job.


when no containers running:

ps fxa | grep docker -A 3 


Working of all components together

Well, To see how all these components work together? We need to initialize a container i.e. Nginx in our case. We will be firing the same command after running an Nginx container.

This shows us that we have two daemons running – the docker daemon and the docker-containerd daemon.

Given that dockerd interacts heavily with containerd all the time and the later is never exposed to the internet, it makes sense to bet that its interface is unix-socket based.

High-Level overview of initializing container

Initializing container to see involvement of all components


docker run --name docker-nginx -p 80:80 -d nginx
docker ps
pstree  -pg | grep -e docker -e containerd 
ps fxa | grep -i “docker” -A 3 | grep -v “java”


By now, it might be clear that dockerd is not only the single component involved while running a container. We got to know what all components are backing a running container beside dockerd and how they work together to manage the lifecycle of a container.

I hope we have a good understanding of the docker components involved. Now it’s time to see things practically on your own with commands discussed in this blog without mugging up theoretical concepts.

That’s all till next time, thanks for reading, I’d really appreciate your feedback, please leave your comment below if you guys have any feedback or any queries.

Happy Containerization !!

References :

My stint with Runc vulnerability

Today I was given a task to set up a new QA environment. I said no issue should be done quickly as we use Docker, so I just need to provision VM run the already available QA ready docker image on this newly provisioned VM. So I started and guess what Today was not my day. I got below error while running by App image.

docker: Error response from daemon: OCI runtime create failed: container_linux.go:344: starting container process caused “process_linux.go:293: copying bootstrap data to pipe caused \”write init-p: broken pipe\””: unknown.

I figured out my Valentine’s Day gone for a toss. As usual I took help of Google God to figure out what this issue is all about, after few minutes I found out a blog pretty close to issue that I was facing

Bang on I got the issue identified. There is a new runc vulnerability identified few days back.

The fix of this vulnerability was released by Docker on February 11, but the catch was that this fix makes docker incompatible with 3.13 Kernel version.

While setting up QA environment I installed latest stable version of docker 18.09.2 and since the kernel version was 3.10.0-327.10.1.el7.x86_64 thus docker was not able to function properly.

So as suggested in the blog I upgraded the Kernel version to 4.x.

rpm –import
rpm -Uvh
yum repolist
yum –enablerepo=elrepo-kernel install kernel-ml
yum repolist all
awk -F\’ ‘$1==”menuentry ” {print i++ ” : ” $2}’ /etc/grub2.cfg
grub2-set-default 0
grub2-mkconfig -o /boot/grub2/grub.cfg

And here we go post that everything is working like a charm.

So word of caution to every even
We have a major vulnerability in docker CVE-2019-5736, for more details go through the link

As a fix, upgrade your docker to 18.09.2, as well make sure that you have Kernel 4+ as suggested in the blog.

Now I can go for my Valentine Party 👫