The first Docker command most people meet is wonderfully persuasive:
docker run --rm hello-world
Docker downloads an image, creates a container, starts a process and prints a cheerful confirmation that everything is working.
A more practical example might be:
docker run --rm nginx:stable-alpine
Nginx appears to have:
- its own filesystem;
- its own processes;
- its own hostname;
- its own network interfaces;
- its own installed packages;
- its own user accounts.
It looks like a small computer.
That resemblance is useful.
It is also where my mental model began to wobble.
I had started describing containers as:
Lightweight virtual machines.
The word lightweight acknowledged that something was different, but the rest of the sentence preserved the wrong architecture.
A conventional virtual machine normally includes a guest operating system and its own kernel.
A standard Linux container does not.
VIRTUAL MACHINE
application
│
guest user space
│
guest kernel
│
virtual hardware
│
hypervisor
│
physical host
STANDARD LINUX CONTAINER
application process
│
shared Linux kernel
│
Linux host or Linux virtual machine
Docker creates the impression of a separate machine by arranging several Linux isolation and resource-management features around a process.
The process receives a restricted and deliberately constructed view of the system.
It does not receive a private kernel.
That gives me the definition I want to keep:
A standard Linux container is a process, or group of processes, running on a shared Linux kernel with selected views, resources and privileges isolated from other processes.
Docker makes those boundaries convenient to declare, package and operate.
The Linux kernel enforces most of them.
A compact map is useful before looking at each mechanism:
| Property | Standard Linux container |
|---|---|
| Process tree | Usually namespaced |
| Mount tree | Namespaced |
| Network stack | Usually namespaced |
| Hostname and selected IPC resources | Namespaced |
| Resource use | Shared and optionally limited |
| Privileged operations | Restricted by credentials, capabilities and policy |
| Kernel | Shared |
| CPU architecture | Shared or emulated |
| Durable data | An external design decision |
| Security strength | Determined by the actual runtime configuration |
The rest of the article explains what those qualifications mean.
1. A container starts with a Linux process
Suppose I run:
docker run --rm alpine:3.20 sleep 300
The version tag is illustrative. Production images should use supported versions and, where reproducibility matters, immutable digests.
Inside the container, sleep appears to be an ordinary process.
From the Linux host, it is also an ordinary process:
ps aux | grep '[s]leep 300'
The process has a host PID.
It is scheduled by the Linux kernel.
It consumes memory managed by that kernel.
Its system calls enter that kernel.
It has not crossed into a private operating-system kernel hidden inside the image.
Conceptually:
CONTAINER VIEW
PID 1
sleep 300
HOST VIEW
PID 28471
sleep 300
Those are two views of the same process.
The container sees PID 1 because Docker placed the process inside a PID namespace.
The host sees PID 28471 because the process also exists in the host’s broader process hierarchy.
This is the first important shift:
Docker does not place the application inside a tiny computer. It starts the application as an isolated Linux process.
The scope is the ordinary Linux container model
This article describes the common OCI-style Linux container created through Docker Engine, containerd and a runtime such as runc.
Other arrangements can add a stronger sandbox or virtual-machine boundary.
Windows containers also have their own host-kernel and isolation models.
Docker Desktop inserts another layer. Native Docker Engine on Linux can run ordinary Linux containers against the host kernel. Docker Desktop runs Linux containers inside a managed Linux environment on macOS, Windows and Linux, so the containers share the Desktop virtual machine or WSL kernel rather than the physical host kernel directly.
The process model remains useful, but the kernel being shared may belong to a managed virtual machine rather than the laptop’s native operating system.
PID 1 is both an application concern and a namespace concern
The first process in a PID namespace becomes PID 1 for that namespace.
It has special responsibilities:
- it adopts orphaned descendants in the namespace;
- it must reap terminated children;
- Linux gives PID 1 special default signal behaviour;
- Docker treats it as the container’s main process.
A shell wrapper can accidentally interfere with signal delivery:
#!/usr/bin/env sh
my-server
A stronger wrapper replaces itself:
#!/usr/bin/env sh
exec my-server
The application then becomes PID 1 and receives signals directly.
An exec-form Dockerfile instruction has the same advantage:
ENTRYPOINT ["/my-server"]
Another option is a small init process:
docker run --init my-image
That init process can forward signals and reap child processes.
This is not container trivia. It affects graceful shutdown, deployment termination, child-process cleanup and data integrity during restart.
A container can isolate a process hierarchy.
The application still needs to behave correctly at the top of that hierarchy.
2. Docker assembles several kernel boundaries
There is no single Linux mechanism called “a container”.
Docker combines several facilities:
| Mechanism | Main responsibility |
|---|---|
| Namespaces | Give processes isolated views of selected system resources |
| Control groups | Account for and limit shared resource consumption |
| Credentials and capabilities | Define user identity and divide privileged operations |
| Seccomp | Restrict which system calls a process may make |
| Linux security modules | Apply mandatory access-control policy |
| Image layers and storage driver | Construct the root filesystem and writable runtime layer |
| Virtual networking | Provide interfaces, addresses, routes and name resolution |
| Runtime configuration | Decide which boundaries are isolated, shared or weakened |
A simplified launch looks like:
DOCKER
│
├── resolves image and configuration
├── prepares the root filesystem
├── creates or joins namespaces
├── configures cgroups
├── applies user and capability settings
├── applies seccomp and security policy
├── configures networking
└── starts the process
│
▼
LINUX KERNEL
Docker provides the user experience and lifecycle.
A runtime such as runc, often reached through components such as containerd, performs the lower-level work according to an OCI runtime specification.
The exact component chain can vary, but the central fact remains:
Docker configuration
becomes
Linux process configuration
Namespaces change perspective
Linux namespaces give a process its own view of part of the operating system.
They do not copy the complete underlying resource.
They change what the process can observe and address.
Common namespace types cover:
- process IDs;
- mount points;
- networking;
- hostnames;
- selected IPC resources;
- user and group IDs;
- cgroup visibility;
- time-related values on supporting kernels.
The exact set and sharing mode depend on the kernel, runtime and Docker configuration.
The word view matters.
A namespace can make the process tree, mount tree or network stack look private without creating private physical hardware beneath it.
3. Namespaces edit the process’s view
Namespaces create several independent boundaries. They can be combined, shared or omitted separately.
PID namespaces isolate process numbering and visibility
A PID namespace gives a group of processes its own numbering hierarchy.
Inside a container:
PID USER COMMAND
1 root nginx
29 nginx nginx: worker process
30 nginx nginx: worker process
The host may see the same processes with different IDs:
PID COMMAND
24180 nginx
24217 nginx: worker process
24218 nginx: worker process
The parent namespace can observe processes in child namespaces.
The child does not receive the same visibility into its parent.
HOST PID NAMESPACE
│
├── host processes
├── container A processes
└── container B processes
CONTAINER A PID NAMESPACE
│
└── container A processes
Processes inside a normal container cannot usually inspect or signal arbitrary host processes.
That changes when configuration deliberately joins the host PID namespace.
Mount namespaces construct a filesystem view
A mount namespace gives the process its own mount table.
Inside the container, / appears to be the root filesystem:
/
├── app
├── bin
├── etc
├── lib
└── usr
That root is assembled from image content, a writable runtime layer and any configured mounts.
It is not the host’s ordinary root directory.
The process can still receive selected host or external paths through bind mounts and volumes:
docker run --rm \
--mount type=bind,source="$PWD",target=/workspace,readonly \
alpine:3.20 \
ls /workspace
The mount namespace controls which paths appear.
The bind mount deliberately adds a bridge into another storage location.
UTS and IPC namespaces isolate selected system identities
A UTS namespace gives the container its own hostname and domain-name view:
docker run --rm \
--hostname catalog-1 \
alpine:3.20 \
hostname
Changing that hostname does not rename the Linux host.
An IPC namespace separates namespace-scoped resources such as System V shared memory, semaphores and message queues, plus POSIX message queues.
These boundaries are selective. Processes can still communicate through paths deliberately made available, including networks, shared files and mounted Unix sockets.
Isolation describes the default view.
Configuration decides which connections are reintroduced.
User namespaces can remap identity
Without user-namespace remapping, UID 0 inside a rootful Linux container may also be UID 0 from the host kernel’s perspective.
Capabilities, seccomp and other controls still restrict what it can do, but the identity remains dangerous when sensitive host resources are exposed.
A user namespace can map container identities to different host identities:
CONTAINER VIEW
UID 0
root
HOST VIEW
UID 100000
unprivileged mapped user
Rootless container operation goes further by running the engine and containers without a root-owned daemon.
User namespaces improve isolation.
They do not make every mount, device, kernel interface or runtime configuration safe automatically.
Permissions must still be evaluated across the mapping boundary.
Namespaces can be shared deliberately
Docker options can selectively weaken the default view isolation:
--pid host
--network host
--ipc host
A container using the host network namespace does not have the usual private network stack.
A container using the host PID namespace can see the host process hierarchy.
The word container does not prove which namespaces are actually private.
4. Images provide a filesystem and startup contract
A Docker image is a packaged filesystem and configuration template.
| Image contains | Image does not contain |
|---|---|
| Read-only filesystem layers | A running process |
| Application binaries and libraries | Live memory |
| User-space files and package metadata | A private kernel |
| Default command and environment metadata | Durable runtime state |
When Docker creates a container, it combines the image with runtime configuration:
| Container runtime state | Source |
|---|---|
| Root filesystem | Image layers plus writable layer and mounts |
| Main process | Image command or runtime override |
| Environment | Image defaults plus runtime settings |
| Networking | Runtime configuration |
| Resource limits | Runtime configuration |
| Credentials and privileges | Image plus runtime configuration |
Storage drivers construct the writable view
Common Docker storage drivers present image layers and a writable container layer as one tree.
With a copy-on-write driver such as overlay2, modifying a file from a lower image layer causes that file to be copied into the writable layer before modification.
CONTAINER FILESYSTEM
│
├── writable container layer
├── application image layer
├── dependency layer
└── base image layer
Other storage drivers may implement the layer relationship differently. The important contract is that each container receives its own writable runtime state above or alongside immutable image content.
The image remains unchanged.
Another container created from the same image receives separate writable state.
The writable layer is not durable application storage
If a database writes its files into the container’s writable layer, removing the container removes that layer.
The image still exists.
The database changes do not live in the image.
For durable data, Docker provides volumes and bind mounts:
docker run \
--name bfstore-mysql \
--mount type=volume,source=bfstore_mysql_data,target=/var/lib/mysql \
mysql:8
The named volume has a lifecycle independent of one container:
| Container lifecycle | Volume lifecycle |
|---|---|
| Created, started, stopped and removed | Exists until deliberately removed |
| Writable layer belongs to one container | Volume can be mounted into replacement containers |
| Suitable for disposable runtime state | Suitable for data that must survive replacement |
This does not make the data self-protecting.
Volumes still require backups, restore tests, permissions, capacity monitoring, migration planning and encryption decisions.
Container removal is not a backup strategy.
Bind mounts deliberately weaken the filesystem boundary
A bind mount exposes a host path directly inside the container:
docker run --rm \
--mount type=bind,source=/srv/config,target=/config,readonly \
my-image
A writable bind mount can modify the source path.
A dangerous mount can expose highly privileged resources.
The Docker Engine socket is the clearest example:
-v /var/run/docker.sock:/var/run/docker.sock
Access to the Engine control socket normally grants the ability to create containers, mount paths and change runtime security settings.
On a native rootful Linux Engine, that commonly amounts to host-root-equivalent control.
On Docker Desktop, it grants powerful control over the Docker Engine and its managed Linux environment. Access to the physical macOS or Windows host still depends on Desktop’s file-sharing and virtualisation boundaries.
A container filesystem is isolated only until configuration intentionally opens a door.
Read-only roots reduce writable surface
A container can be started with a read-only root filesystem:
docker run --rm \
--read-only \
--tmpfs /tmp \
--mount type=volume,source=app_data,target=/var/lib/app \
my-image
This narrows where a compromised process can persist modifications.
It also tests whether the application has a deliberate storage model.
Read-only roots do not prevent every attack.
They reduce accidental and malicious writes within the container filesystem.
5. Networking creates another namespaced view
A network namespace provides its own:
- interfaces;
- IP addresses;
- routing table;
- port space;
- firewall context;
- loopback interface.
Inside a typical container:
lo
eth0
127.0.0.1 refers to that container’s network namespace, not the host and not another container.
This is why two containers can both listen on port 8080.
Docker often connects a container to a host bridge using a virtual Ethernet pair:
CONTAINER
eth0
│
│ virtual Ethernet pair
▼
HOST BRIDGE
│
▼
HOST OR EXTERNAL NETWORK
One end appears inside the container.
The other appears on the Linux host or Desktop virtual machine.
Publishing a port creates host reachability
A process listening on port 80 inside the container is not automatically reachable through host port 80.
Docker can publish it:
docker run --rm \
--publish 8080:80 \
nginx:stable-alpine
This maps a host-side port to the container-side port:
HOST PORT 8080
│
▼
CONTAINER PORT 80
Omitting the host address normally publishes on all host interfaces, potentially including both IPv4 and IPv6 exposure depending on configuration.
Bind to loopback when the service is intended only for the local machine:
docker run --rm \
--publish 127.0.0.1:8080:80 \
nginx:stable-alpine
The application also needs to listen on an address reachable inside the container namespace. Binding only to the container’s own loopback interface can prevent forwarded traffic from reaching it.
Networking isolation and network accessibility are separate decisions.
Docker networks provide scoped connectivity
Containers attached to the same user-defined network can communicate through names resolved by Docker:
docker network create bfstore
docker run -d \
--name catalog \
--network bfstore \
catalog-image
docker run --rm \
--network bfstore \
curlimages/curl \
http://catalog:8080
A container outside that network does not automatically receive the same connectivity.
This provides useful segmentation.
It is not a complete zero-trust security model.
A process on an attached network may communicate with every reachable service unless further controls restrict it.
Network namespaces create separate stacks.
Network policy determines which paths should exist between them.
6. Control groups govern shared resources
Namespaces isolate views.
They do not stop a process from consuming host CPU, memory or process-table capacity.
Control groups, usually called cgroups, organise processes into groups for resource accounting and control.
Docker can apply controls for resources including:
- memory;
- CPU time;
- kernel tasks through the PIDs controller;
- block I/O;
- selected device access through related mechanisms.
Examples include:
docker run --rm \
--memory 256m \
--cpus 0.5 \
--pids-limit 100 \
my-image
The application still uses host CPUs and memory.
The cgroup governs its permitted use.
Memory limits change failure containment
Without a useful memory limit, a leaking process may continue growing until the host experiences severe pressure.
With a limit, memory is accounted against the cgroup.
If the limit is exhausted and the kernel cannot reclaim enough memory, a process in the cgroup may be killed.
APPLICATION MEMORY GROWS
│
▼
CGROUP LIMIT REACHED
│
▼
KERNEL CANNOT RECLAIM ENOUGH
│
▼
PROCESS KILLED
The limit narrows the blast radius.
It does not make the application healthy.
The service still needs memory monitoring, restart policy, request limits, leak investigation and capacity planning.
CPU limits do not create dedicated processors
A setting such as:
--cpus 0.5
usually constrains CPU time through scheduler controls.
It does not reserve half of one physical processor exclusively for the container.
A container can be throttled after using its quota even while the host has other capacity available.
Monitoring should therefore consider both CPU use and CPU throttling.
CPU quota, CPU affinity and dedicated hardware answer different questions.
The PIDs controller counts tasks, including threads
--pids-limit 100 is commonly described as a process limit, but the underlying PIDs controller counts kernel tasks, which includes threads.
A Go service can therefore consume several PIDs even when it appears to be one application process.
Choose the limit from observed workload behaviour rather than assuming one process consumes one PID.
Limits are not scheduler requests
A Docker limit says:
Do not let this container exceed this boundary.
A scheduler request says something closer to:
Place this workload on a machine where this amount of capacity can be planned for it.
Those are related but different.
Several containers on one Docker host can each have individually reasonable limits while collectively exceeding the machine’s capacity.
Resource governance needs both local enforcement and placement planning.
7. Privileges determine what remains possible
Container security is layered.
The relevant question is not only:
Is this process inside a container?
It is:
Which user identity, capabilities, system calls, devices, mounts and kernel interfaces remain available?
Running as non-root still matters
A Dockerfile can define:
USER 10001:10001
This reduces what the application can do if compromised and exposes assumptions hidden by root execution.
A least-privilege application should write only to intended paths, receive only the permissions it needs and avoid unnecessary ownership changes or privileged ports.
User namespaces and rootless operation can strengthen the host boundary further.
No one setting replaces the rest of the model.
Capabilities divide root privileges
Linux capabilities split many privileged operations into smaller units.
Docker removes several capabilities by default and grants a smaller set.
Capabilities can be added:
docker run --cap-add NET_ADMIN my-image
or removed:
docker run --cap-drop ALL my-image
A strong default is to drop all capabilities and add back only those the application genuinely requires.
The important question is not merely whether the process is root inside the container.
It is which kernel privileges that identity can exercise.
Prevent privilege escalation
Docker can set:
--security-opt no-new-privileges=true
This prevents the process and its descendants from gaining additional privileges through mechanisms such as set-user-ID executables.
It is a useful defence when the workload should never acquire more privilege than it had at startup.
A privileged container removes many guardrails
Docker can start a container with:
docker run --privileged my-image
This grants broad device access and relaxes several security controls.
A privileged container should not be interpreted as a normal container, but easier.
On native rootful Linux it is much closer to a host-trusted process with a namespaced presentation.
On Docker Desktop it is highly privileged inside the Docker-managed Linux environment, not automatically an administrator process on the physical macOS or Windows host.
Either way, the boundary is substantially weaker.
Seccomp reduces kernel attack surface
Applications enter the kernel through system calls.
Seccomp can filter those calls.
Docker applies a built-in seccomp profile on supported Linux configurations unless it is replaced or disabled.
APPLICATION
│
│ system call
▼
SECCOMP POLICY
│
├── allowed ──► kernel handles call
└── blocked ──► call rejected
The exact built-in profile evolves with Docker and can vary with selected capabilities.
The durable principle is not one permanent blocked-call count. It is that ordinary workloads should receive only the kernel entry points they need.
Linux security modules add mandatory policy
AppArmor, SELinux and other Linux security modules can constrain access even when ordinary Unix permissions would otherwise allow it.
They are distinct from namespaces:
| Mechanism | Question |
|---|---|
| Namespace | What does the process see? |
| Unix credentials and capabilities | Which identity and privileged operations does it have? |
| Seccomp | Which system calls may it attempt? |
| Linux security module | Which policy-controlled resources may it access? |
| Cgroup | How much shared resource may it consume? |
Strong isolation is layered.
No single mechanism carries the complete boundary.
The shared kernel remains the central security fact
Containers on one Linux host depend on the same kernel:
CONTAINER A PROCESS ──┐
│
CONTAINER B PROCESS ──┼──► SHARED LINUX KERNEL
│
HOST PROCESS ─────────┘
This enables fast startup and low overhead.
It also means a kernel vulnerability can affect the boundary between containers and host.
A container is a useful process-isolation boundary.
It is not automatically the same security boundary as a separate virtual machine.
8. The kernel and machine still matter
An image may be labelled Ubuntu, Alpine or Debian.
That normally describes its user-space filesystem and package ecosystem.
It does not mean the image contains that distribution’s kernel.
UBUNTU CONTAINER
Ubuntu user-space tools
Ubuntu libraries
Ubuntu package metadata
│
▼
shared Linux kernel
Inside the container:
uname -r
reports the kernel supplied by the Linux host or managed Linux virtual machine.
Installing a newer user-space package does not upgrade that kernel.
This matters when software depends on specific system calls, cgroup features, filesystems, security modules or kernel modules.
CPU architecture still matters
A container image contains binaries built for architectures such as amd64 or arm64.
Docker does not transform one instruction set into another merely by starting the image.
Multi-platform image indexes can point to separate variants:
IMAGE INDEX
│
├── linux/amd64 image
└── linux/arm64 image
Emulation can run some foreign-architecture binaries, but it adds another execution layer and may change performance or compatibility.
Containers package user-space dependencies.
They do not erase CPU instruction sets.
Containers and virtual machines are often combined
A cloud platform commonly runs containers inside virtual machines:
PHYSICAL HOST
│
▼
HYPERVISOR
│
├──► VM A
│ ├── container 1
│ └── container 2
│
└──► VM B
├── container 3
└── container 4
The virtual machine provides a separate guest kernel and infrastructure boundary.
Containers provide lightweight process packaging and scheduling inside that VM.
The technologies are not opponents competing for one throne.
They can form different layers of the same system.
Portability has a boundary
A container image can move across compatible Linux environments because it brings its user-space files and startup contract.
It still depends on:
- kernel features;
- CPU architecture;
- runtime capabilities;
- security-module policy;
- available devices;
- filesystem and network behaviour;
- external services and storage.
A portable image is not independent of every host characteristic.
9. Operating and inspecting the process
A container packages and constrains a process.
It does not replace ordinary process operation.
Signals cross the boundary deliberately
When Docker stops a container, it sends a termination signal to the main process:
send SIGTERM
│
▼
wait for the configured grace period
│
▼
send SIGKILL if the process remains
The application should stop accepting new work, complete or cancel eligible work, close listeners, leave consumer groups cleanly and exit before the grace period expires.
Docker does not invent a separate shutdown universe.
It uses ordinary process signals across a namespaced boundary.
Logging leaves the boundary deliberately
Containerised applications often write logs to standard output and standard error.
The runtime or host logging system collects those streams.
The design still needs rotation, storage limits, structured fields, correlation IDs, redaction, central collection and retention.
Observability requires selected information to cross the isolation line.
Health status is not restart policy
A Docker image can define a health check:
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://127.0.0.1:8080/health || exit 1
Docker can report starting, healthy or unhealthy.
A plain Docker Engine restart policy responds to process exit, not merely to an unhealthy result.
An orchestrator or external controller may choose to act on health status.
The application must still define what the probe means:
| Signal | Question |
|---|---|
| Liveness | Can this process continue? |
| Readiness | Should this instance receive traffic? |
| Dependency health | Which external systems are degraded? |
| Business health | Is useful work completing? |
One successful endpoint does not prove that every dependency or business capability is healthy.
Restarting the container is not application recovery
A restart policy can start a new process after the previous one exits.
It does not repair corrupt data, poison messages, incompatible schemas, missing dependencies or unfinished distributed work.
| Layer | Recovery question |
|---|---|
| Container lifecycle | Can the process be started again? |
| Application recovery | What work completed, what remains pending, and what must be repaired? |
The distinction echoes the earlier lesson that retry is not the same as recovery.
Inspect the actual boundary
Docker exposes runtime configuration:
docker inspect my-container
docker top my-container
docker stats my-container
After finding the host PID:
docker inspect \
--format '' \
my-container
a Linux host can inspect namespace links:
ls -l /proc/<PID>/ns
Typical entries include namespace types such as:
cgroup
ipc
mnt
net
pid
user
uts
The container stops being a magical Docker artefact.
It becomes a Linux process with inspectable boundaries.
A small experiment
Start an Alpine container:
docker run -d \
--name isolation-demo \
--hostname tiny-machine \
alpine:3.20 \
sleep 600
Then inspect it:
docker exec isolation-demo hostname
docker exec isolation-demo ps
docker top isolation-demo
docker exec isolation-demo ip addr
docker inspect isolation-demo
Finally:
docker rm -f isolation-demo
The process has not entered another universe.
Its view has been carefully edited.
A possible bfstore container
Suppose bfstore’s catalogue service is packaged into an image.
The versions below are illustrative. A production build should use supported releases and pin important base images by digest.
FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build \
-o /out/catalog-service \
./cmd/catalog-service
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build \
/out/catalog-service \
/catalog-service
USER nonroot:nonroot
ENTRYPOINT ["/catalog-service"]
At runtime:
docker run --rm \
--name bfstore-catalog \
--network bfstore \
--read-only \
--tmpfs /tmp \
--memory 256m \
--cpus 0.5 \
--pids-limit 100 \
--cap-drop ALL \
--security-opt no-new-privileges=true \
--env CATALOG_GRPC_ADDRESS=:50051 \
bfstore/catalog-service:dev
This configuration expresses several boundaries:
| Concern | Runtime choice |
|---|---|
| Process | catalog-service is the main process |
| Filesystem | Image root is read-only |
| Temporary storage | /tmp is an explicit writable tmpfs |
| Network | Attached to the bfstore network |
| Memory | Limited to 256 MiB |
| CPU | Limited to half a CPU’s worth of quota |
| Kernel tasks | Limited through the PIDs controller |
| Capabilities | All dropped |
| Privilege escalation | Disabled |
| Identity | Non-root user from the image |
The service still uses the shared Linux kernel, consumes host resources, depends on reachable services and needs externally managed configuration, logs, metrics and recovery.
The container packages and constrains the process.
It does not complete the platform around it.
Container review checklist
| Concern | Question |
|---|---|
| Process | Which executable becomes PID 1? |
| Signals | Does it handle termination and reap child processes? |
| Image | Which files and runtime dependencies are packaged? |
| User | Can the application run as a non-root identity? |
| User namespace | Is container identity remapped or rootless? |
| Capabilities | Which kernel privileges remain enabled? |
| Privilege escalation | Is no-new-privileges appropriate? |
| Filesystem | Which paths must be writable? |
| Persistence | Which data must survive container replacement? |
| Mounts | Which host or external paths cross the boundary? |
| Network | Which networks may the process join? |
| Ports | Which container ports are published, and on which host addresses? |
| Resources | Which CPU, memory and PIDs limits apply? |
| System calls | Is the default seccomp policy sufficient? |
| Security policy | Can AppArmor, SELinux or another policy narrow access? |
| Devices | Which host devices are exposed? |
| Kernel | Which host-kernel features and versions are required? |
| Architecture | Which CPU platforms does the image support? |
| Secrets | How are sensitive values supplied without baking them into the image? |
| Health | What do liveness and readiness mean for this service? |
| Observability | Where do logs, metrics and traces leave the container? |
| Recovery | What application state remains unresolved after restart? |
| Runtime context | Is this native Linux Engine, Docker Desktop, or another sandboxed runtime? |
A Dockerfile answers how the process is packaged.
Runtime configuration answers how it is isolated.
Application architecture answers whether it remains correct when the process stops.
The mental model I am keeping
My original model was:
CONTAINER
small computer
inside the host computer
The stronger model is:
LINUX HOST OR VM
│
▼
SHARED KERNEL
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
HOST PROCESS CONTAINER A CONTAINER B
│ │
namespaces namespaces
cgroups cgroups
credentials credentials
capabilities capabilities
security policy security policy
image filesystem image filesystem
Namespaces answer:
What can this process see?
Control groups answer:
How much may this process consume?
Credentials, capabilities and security policy answer:
Which operations may this process perform?
The image answers:
Which user-space files and defaults travel with it?
Volumes and mounts answer:
Which data and external paths cross the filesystem boundary?
Virtual networking answers:
Which interfaces, addresses and routes does it receive?
The shared kernel answers the final question:
What remains common beneath every container?
Docker’s achievement is not that it creates a complete computer whenever I type docker run.
It takes ordinary Linux processes and gives them carefully constructed surroundings:
- a private-looking process tree;
- a private-looking filesystem;
- a private-looking network stack;
- a controlled resource budget;
- a reduced set of privileges.
The surroundings are convincing enough that applications can be packaged, moved and operated as discrete units.
But the isolation has edges.
The kernel is shared.
Resources are shared.
Configured mounts and networks cross boundaries deliberately.
Privileges can be added back.
A container is therefore neither merely a process nor truly a machine.
It is a process whose view of the machine has been edited into a smaller, governed world.
And Docker is the tool that makes that editing repeatable.
References and further reading
Docker overview
Introduces Docker images, containers, the daemon and the client-server architecture.
Docker storage drivers
Explains image layers, writable container layers and storage-driver behaviour.
Docker port publishing
Documents host bindings, published ports and their exposure.
Docker resource constraints
Documents memory, CPU and related container resource controls.
Docker runtime metrics
Explains cgroups, resource accounting and namespace considerations.
Docker security
Introduces namespaces, control groups, daemon security, capabilities and other Docker security boundaries.
Docker seccomp profiles
Documents Docker’s use of seccomp system-call filtering.
Docker Desktop for Linux
Explains that Docker Desktop for Linux uses a virtual machine rather than the native host kernel directly.
Docker container security FAQ
Describes security boundaries for Docker Desktop and privileged containers.
Linux namespaces manual
Documents Linux namespace types and their relationships.
Linux control groups v2
Documents the cgroup v2 resource-management model, including the PIDs controller.
Open Container Initiative runtime specification
Defines the configuration and lifecycle expected of OCI-compatible container runtimes.
runc
Provides a low-level OCI runtime for creating and running containers.