Linux Security·8 min read·

Process Isolation and Container Hardening Fundamentals

Apply namespaces, cgroups, and seccomp to harden containers and isolated processes on Linux, reducing the blast radius of any single workload.

NB

Netbay Developer Relations

Netbay Engineering

On this page

The single most effective way to limit damage on a Linux server is to make sure one compromised workload cannot reach everything else. Linux gives you the primitives to do this — namespaces, control groups, and seccomp — and containers are simply these primitives packaged into a convenient form. This guide explains the security fundamentals underneath process isolation: what namespaces and control groups actually grant, how to make containers fail closed without breaking them, and the concrete hardening steps that shrink the blast radius on a shared or multi-tenant host.

Three layers of process isolation NAMESPACES own view of PID, net, mount, user CGROUPS limit cpu, memory prevent cartwheels SECCOMP / CAPS drop syscalls + kernel capability A breach in one workload is contained to its own walls

Namespaces: Partitioning What the Kernel Lets You See

A namespace is a kernel mechanism that gives a group of processes their own isolated view of a global resource. The most security-relevant ones are the PID, mount, network, user, and UTS namespaces. When you run a process in a fresh namespace, it sees only its own process tree, its own filesystem mounts, its own network stack, and its own hostname — even though all share the same underlying kernel. That is the heart of "a container is a process": one contained process cannot kill or observe another simply because PID 1 means different things in each.

Control Groups: Containing the Resource Damage

Namespaces stop visibility; control groups stop runaway resource consumption. A control group assigns processes to a group and enforces limits on CPU, memory, and I/O. Without a memory limit, one compromised or buggy workload can exhaust the whole host and take down its neighbours — a denial of service that crosses the isolation boundary even though no cross-namespace access happened. Proper cgroup limits are the difference between a noisy workload and a host-wide outage.

bash
sudo systemd-run --scope -p MemoryMax=256M -p CPUQuota=50% --   /usr/bin/python3 /srv/svc/worker.py

systemd-run is the cleanest on a systemd host: it launches the command inside a transient scope whose limits — here 256 MB of memory and half a CPU — are enforced by the cgroup machinery. When memory or CPU is exceeded, the kernel reclaims pages or throttles the scheduler instead of letting the workload balloon. That turns an unruly process into a contained one, and containers layer the same controls automatically.

Capabilities: Refuse More Authority Than You Need

Root in a container is not root on the host — not if you do it right. Modern Linux splits root's power into discrete capabilities, and container runtimes drop the vast majority by default, requiring you to opt back into specific ones. The fewer capabilities a process holds, the smaller the kernel surface it can abuse. The practice is explicit: list what you need, grant only that, and verify with the tooling that inspects the running process.

bash
docker run --rm --cap-drop=ALL --cap-add=NET_BIND_SERVICE   -u 10001:10001   --security-opt no-new-privileges   -p 8080:80 nginx:stable

--cap-drop=ALL starts the container with nothing and --cap-add=NET_BIND_SERVICE restores only the one capability needed to bind a low port. -u runs it as an unprivileged UID so even the container's own "root" never maps to privileged authority, and no-new-privileges prevents any binary from gaining capabilities after start. This reads almost like an ideal: minimal caps, non-root user, no privilege escalation path, single exposed port.

seccomp: Filter the Syscall Attack Surface

Capabilities are coarse; seccomp filters are fine-grained. Every process reaches the kernel through syscalls, and seccomp lets you allow only the ones a workload actually needs, blocking everything else before it touches the kernel's attack surface. A web server that needs a known handful of syscalls has no business calling others, and seccomp enforces that — containers even ship default profiles that block a long list of dangerous calls out of the box.

bash
unshare --user --map-root-user -- "echo 'user namespace enabled'"
strace -f -c docker run --rm alpine true

The first line shows that even running "as root" in a user namespace is genuinely unprivileged from the host's perspective — the mapped root is confined. The second uses strace to count the syscalls a trivial workload makes, the practical way to build a tight seccomp profile: observe the real footprint, then permit only that.

Drop Root from Runtime, Not Just the Image

The most common container hardening miss is that the image runs as root. Even with capabilities dropped, an image that executes as uid 0 inside the namespace leaves little room for error, and the moment a capability slips through, that root becomes leverage. The robust fix is to run the container as an unprivileged user everywhere — in the image's USER directive and the runtime flag — so the process itself never has the privilege to abuse.

dockerfile
FROM node:20-slim
RUN useradd --create-home appuser
USER appuser
COPY --chown=appuser:appuser . /app
WORKDIR /app
CMD ["node", "server.js"]

The USER directive and the --chown copy make the runtime non-root by construction instead of by accident. Combined with the capability drop, this is defense in depth: even if the executable is compromised, it holds neither capabilities, nor root, nor the host's privilege, so the worst-case is a contained run inside one namespace with one workload's resources.

Read-Only Roots and Minimal Images

Two more cheap hardening layers finish the picture. Mount the filesystem read-only so a compromised process cannot write its own persistence into the image it runs from, and keep images small so there is less running code to exploit. A read-only root plus an ephemeral writable volume means a breach can modify nothing permanent — turning many incidents from persistent infections into throwaway containers.

bash
docker run --rm --read-only --tmpfs /tmp --cap-drop=ALL   -u 10001:10001 alpine sh -c "touch /tmp/ok && ls /"

--read-only mounts the entire root filesystem without write access, and --tmpfs /tmp gives the one place that does need to be writable a memory-backed location that disappears with the container. The touch succeeds only under /tmp; any attempt to write elsewhere fails outright. That single flag is one of the highest-value, lowest-cost hardening steps available for containers.

Takeaway

Namespaces, control groups, capabilities, and seccomp together partition a Linux host so that no single workload holds the keys to everything else. Container hardening is the practical application: drop caps, run non-root, filter syscalls, keep the root read-only, and limit resources — and a breach becomes a contained problem instead of a platform compromise. Netbay's VPS plans give you a full Ubuntu or Rocky host where you control namespaces, cgroups, and seccomp from the first boot — netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance