Do You Think Your Containers Are Secure? No, They Are Not.

Table of contents

Most devs still think Docker gives them isolation. It doesn't.
Containers aren’t security boundaries. They’re process wrappers with just enough abstraction to make you feel safe. But if you're running containers with root
, broad capabilities, and zero hardening, you're one kernel exploit away from turning your host into an open playground.
1. Containers Are NOT Security Boundaries
Docker leverages Linux namespaces (PID, NET, IPC, UTS, MNT, USER) and cgroups to isolate processes and control resource consumption. But this is process isolation, not security isolation.
If a container process escapes its namespace, it can interact with the host system.
Technical Detail:
Namespaces define what a process can see.
Cgroups define how much a process can use.
Nothing enforces "cannot affect the host" unless explicitly configured.
Example: PID Namespace Escape
If security modules aren't enforced, a container breakout exploit can escape to the host PID namespace and read or kill host processes
2. UID 0 in the Container == UID 0 on the Host
Containers often run as root (UID 0
) inside. Unless user namespaces are enabled, this maps directly to UID 0
on the host.
Implications:
If a container process gains the capability to escape the namespace, you now have a root process on the host.
A single
docker exec
could be used by an attacker to pivot.
Hardening:
Use user namespace remapping:
"userns-remap": "default"
In /etc/docker/daemon.json
or with --userns-remap
.
This maps container UID 0 to an unprivileged UID on the host, e.g., UID 100000.
Caveats:
Not all images support this due to filesystem or permission assumptions.
Needs volume mounts to be UID-translated.
3. Containers Run with Dangerous Linux Capabilities
Docker grants a set of ~14 capabilities by default, including several that should never be present in production:
Risky Capabilities:
CAP_SYS_ADMIN
: Mount filesystems, change network interfaces, god mode.CAP_NET_RAW
: Use raw sockets (sniffing, spoofing).CAP_SYS_MODULE
: Load/unload kernel modules.
Safer Practice:
Drop all and add only what’s needed:
--cap-drop=ALL --cap-add=NET_BIND_SERVICE
Use the principle of least privilege — containers should behave like jailed processes, not privileged guests.
Audit Tip:
Run this to list capabilities inside a container:
capsh --print
4. No Seccomp, AppArmor, SELinux? You’re Exposed
By default, Docker does not enforce MAC (Mandatory Access Control) systems like Seccomp, AppArmor, or SELinux. These systems are critical to reducing the kernel syscall and device attack surface.
Seccomp:
Filters syscalls. Prevents the use of dangerous syscalls like ptrace
, keyctl
, mount
, etc.
Apply via:
--security-opt seccomp=/path/to/seccomp-profile.json
AppArmor (Ubuntu, Debian):
Confinement profiles per container:
--security-opt apparmor=docker-default
SELinux (RHEL, Fedora):
--security-opt label:type:container_t
Minimum Secure Profile Example:
--security-opt seccomp=default.json \
--security-opt apparmor=docker-default \
--read-only \
--no-new-privileges
This ensures containers cannot gain new privileges, even via setuid binaries or exec.
5. Still Using --privileged
? You’ve Already Lost
--privileged
disables all kernel isolation:
Grants ALL capabilities
Mounts all host devices
Disables seccomp
Bypasses AppArmor and SELinux
This flag is intended for debugging, not production.
If you see this in a production docker run
, you're no longer containerizing. You’re just running a root process in a chroot jail.
Example: Avoid this
docker run --privileged myapp
Safer Alternative:
Use
--device
to grant specific device accessUse
--cap-add
for fine-grained capabilitiesAlways combine with
--security-opt no-new-privileges
6. Bonus: Secure Container Hardening Checklist
Security Feature | Status | Recommended Action |
Run as non-root | MUST | Set USER in Dockerfile or use --user |
Drop Linux capabilities | MUST | --cap-drop=ALL + add only what’s needed |
Enable Seccomp | HIGHLY RECOMMENDED | Use hardened seccomp profiles |
Enable AppArmor/SELinux | HIGHLY RECOMMENDED | Enforce profiles with --security-opt |
Read-only filesystem | RECOMMENDED | --read-only |
No new privileges | RECOMMENDED | --security-opt no-new-privileges |
Avoid --privileged | MUST | Never use in production |
Use user namespace remapping | RECOMMENDED | Enable via Docker daemon config |
Final thoughts
Containers are convenient, but they are not secure by default. Running containers with root access, broad capabilities, and no MAC enforcement is a recipe for lateral movement and privilege escalation.
If you’re deploying containers in production without hardening them, you’re not isolating, you’re gambling.
Take control of your container security. Audit your stack, enforce restrictions, and treat every container as if it were compromised by design.
I help engineering teams build secure, production-grade systems that can easily scale.
If your containers are running as root, missing seccomp, or leaking capabilities, you're not shipping software, you're shipping liabilities.
→ Follow me on LinkedIn for brutally practical insights
→ Subscribe to this blog for deep dives, not developer fluff
→ Reach out if your stack needs clarity, performance, or a no-bullshit architecture review
I don’t write theory. I ship battle-tested systems.
Subscribe to my newsletter
Read articles from Kaan Yagci directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Kaan Yagci
Kaan Yagci
Senior Platform Engineer. Infra and programming languages nerd. I write about the stuff nobody teaches: how things really work under the hood, containers, orchestration, authentication, scaling, debugging, and what actually matters when you’re building and running real systems. I share what I wish more real seniors did: the brutal, unfiltered truth about building secure and reliable systems in production.