What a container actually is
No such kernel object exists - it is namespaces plus cgroups plus a filesystem.
Open this lesson in the learning hubKey points
- There is no container primitive in the Linux kernel. A container is an ordinary process with restricted visibility and restricted resources, assembled from features that predate Docker.
- Namespaces control what a process can see. PID gives it its own process tree so it believes it is PID 1; NET gives it its own interfaces and routing table; MNT its own filesystem view; UTS its own hostname; IPC, USER and CGROUP the rest.
- cgroups control what it can consume - CPU, memory, block I/O, process count. Namespaces are about isolation, cgroups about limits, and confusing the two explains many wrong mental models.
- Because it is just a process, a container has no boot, no kernel of its own, and shares the host kernel. That is why a container starts in milliseconds and why a kernel exploit escapes it.
- PID 1 has special semantics: it does not get default signal handlers, and it must reap orphaned children. A JVM as PID 1 that ignores SIGTERM will be SIGKILLed, which is the usual cause of ungraceful shutdown.
- The USER namespace is what makes rootless containers possible: root inside can map to an unprivileged uid outside, so a container escape does not hand over the host.
Example
# A container is a process. Look at it from the host:
$ docker run -d --name app nginx
$ docker inspect -f '{{.State.Pid}}' app
# 48213
$ ps -o pid,ppid,cmd -p 48213 # an ordinary process on the host
$ ls -l /proc/48213/ns/ # ... with its own namespaces
# ipc -> ipc:[4026532other]
# mnt -> mnt:[4026532...]
# net -> net:[4026532...]
# pid -> pid:[4026532...]
# Inside, it believes it is PID 1:
$ docker exec app ps aux
# PID USER COMMAND
# 1 root nginx: master process
# The limits, in the cgroup:
$ cat /sys/fs/cgroup/system.slice/docker-<id>.scope/memory.max
$ cat /sys/fs/cgroup/system.slice/docker-<id>.scope/cpu.max
# 50000 100000 -> 0.5 CPU: 50ms of every 100ms period
---
# THE PID 1 PROBLEM. Shell form makes the shell PID 1, not the JVM:
#
# CMD java -jar app.jar -> /bin/sh -c "java -jar app.jar"
# sh is PID 1 and does NOT forward SIGTERM
# -> no graceful shutdown, SIGKILL at timeout
#
# CMD ["java", "-jar", "app.jar"] -> java IS PID 1, receives SIGTERM
# Exec form, plus an init to reap zombies if the app spawns children:
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
# docker run --init ... # or tini as PID 1
A container is a process with namespaces and cgroups - so it shares the host kernel, and its PID 1 must handle SIGTERM itself.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Docker course, and every lesson in it is listed on the Docker contents page.