# Namespaces and cgroups

> A container is a normal Linux process with a restricted view (namespaces) and a budget (cgroups).

Concept · Running systems · https://levelcode.org/concepts/container-isolation

A container isn't a small virtual machine. It's an **ordinary Linux process** that the kernel shows a restricted view of the system and puts on a resource budget. Two kernel features do most of the work.

## Namespaces: what the process can see

Each namespace type gives the process its own private copy of one kind of system resource:

| Namespace | Isolates |
|---|---|
| `pid` | Process IDs: the container sees itself as PID 1 and can't see host processes |
| `mnt` | Mount points: its own root filesystem (the image) |
| `net` | Network interfaces, IPs, ports, routing tables |
| `uts` | Hostname |
| `ipc` | Shared memory and message queues |
| `user` | User IDs: root inside can map to an unprivileged user outside |
| `cgroup` | Its view of the cgroup tree |

```bash
docker run --rm alpine ps      # shows only the container's processes
```

## cgroups: what the process can use

Control groups limit and account for **CPU, memory, I/O and number of processes**:

```yaml
resources:
  requests: { cpu: 250m, memory: 256Mi }   # scheduling guarantee
  limits:   { cpu: "1",  memory: 512Mi }   # hard ceiling
```

- **Over the memory limit, the process is OOM-killed.**
- **Over the CPU limit, it's throttled,** which shows up as latency spikes, not errors.
- A `pids` limit stops a fork bomb from taking down the node.

Language runtimes have to *read* these limits. Go reads cgroup CPU limits for `GOMAXPROCS` from Go 1.25, and `GOMEMLIMIT` should be set a little below the memory limit.

## The security boundary is thinner than a VM's

All containers on a host **share one kernel**. A kernel vulnerability, or a container given too much privilege, can lead to an escape onto the host. The defaults are reasonable, and most hardening is about not undoing them:

- **Don't run as root.** Set `USER` in the image and `runAsNonRoot: true`. Use user namespaces where available.
- **Drop Linux capabilities** (`drop: [ALL]`) and add back only what's needed.
- **Never use `--privileged`** or mount the Docker socket or host paths into an application container. Either one is effectively root on the host.
- **Keep seccomp and AppArmor/SELinux profiles on,** with `RuntimeDefault` at minimum, and use a read-only root filesystem.
- **For untrusted code,** add a stronger boundary: gVisor, Kata Containers or Firecracker microVMs.

All of this is [least privilege](https://levelcode.org/concepts/least-privilege) applied to a process. The [container security lesson](https://levelcode.org/roadmaps/devsecops/container-security) adds image scanning and minimal base images.
