When you execute a simple command like docker run -d -p 8080:80 nginx on your laptop, a lot happens under the hood within a fraction of a second. To a beginner, it feels like running a lightweight virtual machine. But if you are a computer science student studying operating systems, you might wonder: how does a single process running on your host machine suddenly look and feel like an entirely isolated computer?
Unlike traditional virtual machines that virtualize hardware and run a complete guest operating system, Docker containers share the host machine's kernel. They achieve isolation and resource control using native Linux kernel features. Let us trace step-by-step what actually happens inside your operating system when you run a container.
1. The Request Hits the Docker Daemon
When you type docker run in your terminal, your command-line interface (CLI) does not actually run the container itself. Instead, the Docker CLI acts as a client that talks to the Docker daemon (dockerd) through a REST API over a Unix socket (/var/run/docker.sock).
The daemon receives your request, validates the instructions, and prepares to construct the container environment based on the specified image, network settings, and volume mounts.
2. Setting Up the Union File System
Before any process starts, Docker needs to set up the container's file system. This is where Docker images and layers come into play.
An image is just a stack of read-only layers. When you run a container, Docker adds a thin read-write layer on top of those read-only image layers using a union mount filesystem (like OverlayFS).
- Read-only layers: Contain the base operating system binaries, dependencies, and your application code. These are shared across multiple containers if they use the same image, saving disk space and memory.
- Read-write layer: Captures all changes made while the container is running—such as writing log files, modifying configuration files, or downloading packages. If you delete the container, this layer disappears unless mounted to a persistent volume.
3. Creating Kernel Namespaces (Isolation)
Once the file system is ready, Docker asks the Linux kernel to create a set of namespaces for the new process. Namespaces provide isolation, making it look to the process like it has its own dedicated system resources.
Docker utilizes several types of Linux namespaces:
- PID Namespace: Isolates the process ID space. Inside the container, your application runs as PID 1. It cannot see processes running on the host machine or in other containers.
- NET Namespace: Isolates the network interfaces, IP routing tables, and port numbers. This is why a container can run a web server on port 80 without conflicting with another web server running on your host machine's port 80.
- MNT Namespace: Isolates the mount points (file system view). The container cannot see the host's root directory unless explicitly shared via volumes.
- IPC Namespace: Isolates Inter-Process Communication resources like POSIX message queues.
- UTS Namespace: Isolates hostname and domain name settings.
- USER Namespace: Isolates user and group IDs, allowing root access inside the container to map to a non-root user on the host.
Thanks to namespaces, your container feels like an isolated machine, even though it shares the same kernel.
4. Applying Control Groups (Resource Limiting)
Isolation is great, but what prevents a runaway container process from consuming 100% of the host machine's CPU and RAM, crashing your entire computer?
Enter cgroups (Control Groups).
Cgroups are another Linux kernel feature used to allocate, restrict, and monitor hardware resources. When you pass flags like -m 512m or --cpus 1.5 to docker run, Docker configures the cgroups subsystem for that container's process group.
- Memory cgroups: Track and limit memory usage. If your container exceeds the limit, the kernel invokes the Out-Of-Memory (OOM) killer and terminates the process.
- CPU cgroups: Allocate CPU shares or quotas, ensuring your container gets only its fair share of processing power.
5. Network Setup and Port Mapping
If you specified port bindings (like -p 8080:80), Docker sets up networking rules before launching the application process.
- Docker creates a virtual Ethernet bridge device on the host, typically named
docker0. - It creates a veth (virtual Ethernet) pair: one end stays in the host's bridge, and the other end is placed inside the container's network namespace.
- iptables rules are added on the host to route traffic arriving at host port
8080to the container's internal IP address on port80.
6. Executing the Entrypoint Process
With namespaces isolating the environment, cgroups constraining resources, and the union file system providing the root directory, Docker is finally ready to launch your application.
It invokes a system call equivalent to execve, replacing the container setup process with your specified command or entrypoint (e.g., node server.js, python app.py, or nginx). Your application is now running as a standard Linux process, monitored and managed by the container runtime.
Why This Matters for Engineering Students
Understanding what happens under the hood when a container runs bridges the gap between software engineering and operating systems theory. When debugging production issues, understanding namespaces explains why a container cannot see host files, and cgroups explain why your application keeps getting killed with an Exit Code 137 (OOM error).
Containers are not magic—they are just clever combinations of core Linux primitives: namespaces for isolation, cgroups for resource control, and union file systems for storage. Knowing these fundamentals makes you a stronger developer, capable of troubleshooting complex deployments with confidence.

