← Blog

Container Runtime Security: Protecting Running Containers

A practical guide to container runtime security: isolation primitives, hardening flags, escape vectors, and detection for containers already running in production.

Bruno Baldo·Oct 5, 2026·Updated Sep 14, 2026·9 min read·Reviewed by Rainforest Technologies

Container runtime security is the practice of protecting containers while they are actually running, as opposed to while they are being built or stored. It matters because a container that shipped from a clean, fully scanned image is not safe forever. The moment it starts serving traffic it becomes a live target: its running dependencies can be hit by a newly disclosed zero-day, its filesystem can drift as an attacker writes new binaries, and any weakness in the host's isolation can be turned into a path out of the container. This guide is a deep dive into runtime container protection — the isolation primitives that box a container in, the Docker runtime hardening flags that tighten them, the container escape vectors you need to close, and the detection and response that catch what slips through.

This article is part of our Docker container security cluster. If you have not yet hardened the image itself, pair it with our guide to Dockerfile security best practices, and if you run on Kubernetes, read it alongside Kubernetes pod security.

Why runtime security matters

It is tempting to think that security ends when a scanned image passes the pipeline. It does not. A build-time scan is a snapshot of a moment that is already in the past by the time the container is live.

Three things change once a container is running. First, new vulnerabilities are disclosed constantly. The library that was clean when you built the image last month may have a critical CVE announced today, and the running container is exposed until it is rebuilt and redeployed. Second, containers drift. An attacker who gains code execution inside a container can install tools, write payloads, spawn shells, and open network connections — none of which existed in the original image. The gap between what you shipped and what is now running is precisely where an intrusion lives. Third, the isolation between container and host is only as strong as its configuration. A container is not a virtual machine; it shares the host kernel, and a single over-broad flag can collapse the boundary entirely.

Runtime security is the layer that assumes compromise will eventually happen and works to keep it contained and visible.

The isolation primitives — and how to tighten them

Containers are isolated by Linux kernel features, not by a hypervisor. Understanding these primitives is the whole game, because hardening a container means tightening each one past its default.

Namespaces give a container its own view of the system — its own process tree, network stack, mount table, and user IDs. They are what make a container feel like a separate machine. The default is usually sound, but do not weaken it: sharing the host's PID, network, or IPC namespace with the container reintroduces exactly the visibility namespaces exist to remove.

cgroups (control groups) cap how much CPU, memory, and I/O a container can consume. This is a security control, not just an operations one: a container with no limits can exhaust a host and starve every neighbor, which is a denial-of-service condition whether it arrives by bug or by malice. Set explicit limits:

docker run --memory=256m --cpus=0.5 --pids-limit=100 myapp:1.4.2

Linux capabilities slice root's power into discrete privileges. By default Docker grants a container a moderate set, but most applications need almost none of them. The right posture is to drop everything and add back only what is genuinely required:

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp:1.4.2

And never reach for --privileged, which disables most of these protections at once (more on that below).

seccomp filters the system calls a container may make. Docker ships a default seccomp profile that already blocks dozens of dangerous syscalls; the mistake is running with --security-opt seccomp=unconfined, which turns that filter off. Keep the default, or supply a tighter custom profile for sensitive workloads.

AppArmor and SELinux are mandatory access control systems that constrain what files and resources a process can touch, independent of Unix permissions. Docker applies a default AppArmor profile where available; on SELinux hosts, labels enforce a similar boundary. Leave them enabled, and write a custom profile when a workload warrants one.

Two more flags belong in every hardened docker run:

docker run \

  --read-only \

  --tmpfs /tmp \

  --security-opt no-new-privileges \

  --cap-drop=ALL \

  myapp:1.4.2

`--read-only` makes the container's root filesystem immutable so an attacker cannot drop a payload or overwrite a binary; mount a `--tmpfs` for the handful of paths that genuinely need to be writable. `--security-opt no-new-privileges` stops a process from gaining more privileges than its parent — for example, through a setuid binary — which shuts down a common escalation step.

The Docker socket is a runtime risk

One misconfiguration deserves its own section because it is both common and catastrophic: mounting the Docker socket.

The Docker daemon listens on /var/run/docker.sock, and anything that can talk to that socket can control the daemon — which means it can start a new container, mount the host's root filesystem into it, and run as root on the host. Mounting the socket into a container (-v /var/run/docker.sock:/var/run/docker.sock) is therefore equivalent to granting that container full control of the host and every other container on it.

CI runners, monitoring agents, and dashboard tools often ask for the socket for convenience. Treat every such request as a host-takeover risk. Prefer a socket proxy that exposes only the specific, read-only API calls a tool actually needs, run the workload rootless, or use a daemonless tooling approach. If a container truly must manage containers, isolate it and never expose that capability to anything handling untrusted input.

Rootless runtime and user namespaces

By default the Docker daemon runs as root, and a process that escapes a container often lands with root-equivalent power on the host. Two mechanisms shrink that blast radius.

User namespaces remap the container's root (UID 0) to an unprivileged UID on the host. Even if a process believes it is root inside the container, to the host kernel it is a nobody user with no meaningful privileges — so an escape yields far less. Enable this with the daemon's userns-remap setting.

Rootless mode goes further and runs the entire container runtime as a non-root user, so the daemon itself never holds root. It closes off the class of attacks that target the privileged daemon directly. Rootless mode has some feature trade-offs, but for many workloads it is the single highest-leverage runtime hardening step available.

Container escape vectors and their mitigations

A container escape is when a process breaks out of its isolation and gains access to the host or to other containers. Almost every real-world escape comes from one of a few sources:

  • Privileged containers. --privileged grants nearly all capabilities, disables seccomp and AppArmor confinement, and exposes host devices. From a privileged container, escaping to the host is often trivial. Mitigation: never run privileged; drop capabilities and add back the minimum.
  • Host mounts. Bind-mounting a sensitive host path — /, /etc, /var/run, or a device node — lets a compromised container read or tamper with the host directly. Mitigation: mount only what is needed, read-only wherever possible, and never mount the root filesystem or the Docker socket into untrusted workloads.
  • The exposed Docker socket. Covered above — a direct route to host root. Mitigation: keep the socket out of containers, or front it with a restrictive proxy.
  • Kernel exploits. Because every container shares the host kernel, a kernel-level vulnerability can be exploited from inside a container to break the boundary. Mitigation: patch the host kernel promptly, keep seccomp enabled to shrink the reachable syscall surface, and consider a sandboxed runtime that adds a layer between container and kernel for high-risk workloads.

The pattern across all four is the same: the escape is only possible because a default was loosened. Runtime hardening is largely the discipline of not loosening them.

Runtime detection and response

Hardening reduces the odds of a successful attack; it does not eliminate them, and it tells you nothing when one is under way. That is the job of runtime detection.

Behavioral and syscall monitoring watches what a container actually does against a model of what it should do. A web server that suddenly spawns a shell, launches a package manager, or executes a compiler is behaving abnormally, and a syscall-level monitor can flag or block that in real time. This is the model popularized by open-source syscall-monitoring tools: define rules for suspicious behavior — an unexpected outbound connection, a write to a sensitive path, a privilege-escalation attempt — and alert the moment one fires.

Anomaly detection builds a baseline of normal process, file, and network activity for each workload and surfaces deviations from it, which is useful for catching the attacks no explicit rule anticipated.

Drift detection compares the running container against the image it was built from. Because a well-built container should be immutable at runtime, any new binary, modified file, or unexpected process is a strong signal that something has changed that should not have — and drift detection turns that principle into an alert.

None of this is useful without logging. Ship container and host logs — process execution, network connections, file changes, and daemon events — to a central store the containers themselves cannot reach, so an attacker who compromises a container cannot erase their tracks. When an incident happens, that log trail is the difference between a clean investigation and a guess. Build an incident-response plan around it: how you isolate a suspect container, preserve its state for forensics, and rotate any credentials it could have touched, before you kill it.

The tie to Kubernetes runtime

Everything here applies just as much when a container runs inside a Kubernetes pod — the primitives are the same, only the interface changes. The docker run flags in this article map onto fields in a pod's securityContext: dropping capabilities, running as non-root, a read-only root filesystem, allowPrivilegeEscalation: false, and a seccomp profile. If you operate on Kubernetes, read this alongside our guide to Kubernetes pod security, which covers how the platform enforces these same runtime protections across a cluster.

How Rainforest helps

Rainforest brings runtime-relevant security into the developer workflow rather than bolting it on after deployment. Our container security scanning inspects your images and their configuration for the exact issues this article covers — privileged flags, missing capability drops, an exposed Docker socket, absent resource limits, root execution, and dependencies carrying known vulnerabilities — and surfaces them in the pull request, before the container ever runs. Because new vulnerabilities are disclosed after an image ships, that scanning is continuous: a dependency that becomes vulnerable next week is flagged against images already in your registry, so you know what to rebuild.

The result is one consistent picture across the lifecycle: the same hardening you enforce at runtime is checked as code, so a misconfiguration is caught while it is still a small change and not yet a live incident. It is one part of our broader application security testing platform.

If you want to see how that fits your pipeline, book a demo and we will walk through your own images and run configurations.

Frequently asked questions

What is container runtime security?

Container runtime security is the protection of containers while they are running in production, as distinct from securing the image at build time or in the registry. It covers tightening the kernel isolation primitives a container depends on — namespaces, cgroups, capabilities, seccomp, and mandatory access control — and detecting and responding to malicious behavior in containers that are already live. It exists because a clean image can still be attacked at runtime through newly disclosed vulnerabilities, filesystem drift, or a weak isolation boundary.

What is a container escape?

A container escape is when a process breaks out of its container's isolation and gains access to the host system or to other containers on it. Because containers share the host kernel, an escape frequently means host compromise, and host compromise puts every other container on that host at risk. The common causes are running privileged containers, mounting sensitive host paths, exposing the Docker socket, and unpatched kernel vulnerabilities.

Why is mounting the Docker socket dangerous?

The Docker daemon is controlled through /var/run/docker.sock, and anything that can reach that socket can command the daemon — including starting a new container that mounts the host's filesystem and runs as root. Mounting the socket into a container therefore effectively grants that container full control of the host and every container on it. If a tool needs it, front the socket with a restrictive proxy that exposes only the specific calls required, rather than mounting it directly.

What does --privileged do and why avoid it?

The --privileged flag gives a container nearly all Linux capabilities, disables the default seccomp and AppArmor confinement, and exposes the host's devices. In effect it removes most of the boundaries that separate the container from the host, which makes escaping to the host trivial for an attacker who gains code execution inside. Instead of running privileged, drop all capabilities with --cap-drop=ALL and add back only the specific ones a workload genuinely needs.

How do I detect attacks on running containers?

Use runtime detection: behavioral and syscall monitoring that flags a container doing something abnormal, such as spawning a shell or opening an unexpected network connection; anomaly detection that learns each workload's normal behavior and surfaces deviations; and drift detection that compares the running container against its original image to catch new binaries or modified files. Pair all of it with centralized logging the containers cannot reach, so you have a trustworthy trail for incident response.

Bruno Baldo

Written by

Bruno Baldo

CMO

Um pouco de marketing e um pouco de curiosidade e temos a receita pra criar um apaixonado por cyber!

Keep reading