Runtime Security: Non-root Containers, Seccomp, AppArmor, and Least Privilege
Informational article in the Deploying Python Apps with Docker and CI/CD topical map — Security, Observability, and Scaling content group. 12 copy-paste AI prompts for ChatGPT, Claude & Gemini covering SEO outline, body writing, meta tags, internal links, and Twitter/X & LinkedIn posts.
Runtime security non-root containers seccomp apparmor least privilege is achieved by running processes inside the container as a non-UID 0 user (not UID 0), applying a Seccomp BPF syscall filter to reduce the syscall surface, and enforcing an AppArmor Linux Security Module profile to constrain file, network and capability access. Running as non-UID 0 avoids the special privileges of UID 0 (root) inside the namespace, while Seccomp uses BPF programs to whitelist or block syscalls and AppArmor applies LSM rules at the kernel boundary; these three controls together implement the principle of least privilege.
The mechanism works because the layers operate at different levels: Docker and Kubernetes expose runtime flags and security-opt hooks, Seccomp enforces syscall filtering with a seccomp profile (JSON BPF rules), and AppArmor provides path- and capability-level confinement as an LSM. In practice, a Dockerfile should drop privileges in Dockerfile by using USER and chown during build, and the runtime can further drop Linux capabilities via docker run --cap-drop=ALL and selectively add needed CAP_* values. Tools such as docker, podman, systemd-run, and the Open Container Initiative (OCI) runtime spec are commonly used to apply and validate profiles.
A common nuance is that Seccomp and AppArmor are complementary, not interchangeable, and a persistent mistake is running containers as root and relying only on the default docker-default seccomp profile. For example, a Django or Flask deployment that bind-mounts a volume and then runs as root often forces developers to change filesystem permissions instead of switching to USER, creating persistent root-owned artifacts. Likewise, retaining docker-default without removing high-risk syscalls or failing to drop CAP_SYS_ADMIN (widely regarded as effectively all-powerful) leaves containers more permissive than intended. Container hardening for Python apps requires the trio: non-root containers, a tightened seccomp profile, and an AppArmor profile tuned to the app’s I/O and networking pattern.
The practical takeaway is to adopt a repeatable sequence: set USER in the Dockerfile, use --cap-drop=ALL with selective --cap-add if necessary, supply a tailored seccomp profile, and apply an AppArmor profile validated in CI with runtime smoke tests and least privilege docker checks. This page provides a step-by-step framework for implementing non-root users, crafting and testing seccomp profiles, applying AppArmor confinement, and dropping Linux capabilities in CI/CD.
- Work through prompts in order — each builds on the last.
- Click any prompt card to expand it, then click Copy Prompt.
- Paste into Claude, ChatGPT, or any AI chat. No editing needed.
- For prompts marked "paste prior output", paste the AI response from the previous step first.
docker runtime security non root
runtime security non-root containers seccomp apparmor least privilege
authoritative, practical, evidence-based
Security, Observability, and Scaling
Python developers and DevOps engineers who build and deploy containerized Python apps with Docker and CI/CD; intermediate knowledge (comfortable with Dockerfiles and basic Linux concepts) seeking pragmatic runtime security steps
A concise, hands-on guide that ties non-root container practice to Seccomp and AppArmor profiles specifically for Python workloads, with concrete Dockerfile snippets, runtime flags, CI validation steps, and a least-privilege checklist tailored to common Python pitfalls.
- non-root containers
- seccomp profile
- AppArmor profile
- least privilege docker
- container runtime security
- drop Linux capabilities
- Linux capabilities
- drop privileges in Dockerfile
- Docker security best practices
- runtime confinement
- container hardening for Python apps
- Running containers as root by default and not setting USER in the Dockerfile, which leaves files owned by root and forces insecure workarounds.
- Confusing Seccomp and AppArmor: treating them as interchangeable instead of complementary syscall filtering (Seccomp) vs LSM-level confinement (AppArmor).
- Creating overly permissive Seccomp profiles (e.g., using docker-default unchanged) without explicitly denying dangerous syscalls for Python workloads.
- Forgetting to test profiles in CI/CD: not failing builds when AppArmor/seccomp validation fails or when a non-root smoke test detects permission issues.
- Not accounting for Python-specific filesystem behavior (pip cache, virtualenv creation, compiled .pyc files) which can break when dropping privileges if filesystem ownership isn't handled.
- Assuming Kubernetes PodSecurityPolicy is present: PSP is deprecated in many clusters and needs Pod Security Admission or OPA/Gatekeeper replacements.
- Not dropping Linux capabilities (CAP_NET_ADMIN, CAP_SYS_ADMIN) and relying solely on non-root which still may leave broad privileges.
- In Dockerfiles, use multi-stage builds and set USER after all root-only steps; copy artifacts out of root-owned build stages into a non-root runtime image to avoid chown overhead.
- Generate a conservative Seccomp profile by starting from Docker's default seccomp.json, then run your Python app under audit mode (strace or seccomp-audit tools) to discover and then explicitly allow only required syscalls.
- Use AppArmor in complain mode first on a CI runner: deploy a test pod/container with the profile in complain mode and collect logs (journalctl/audit) to iterate until safe to enforce.
- Add small CI jobs that run your image with a test script that verifies UID/GID, writes to expected dirs, and performs network calls—fail the pipeline if the container needs root to start.
- Drop unnecessary capabilities explicitly (e.g., --cap-drop=ALL --cap-add=NET_BIND_SERVICE if absolutely needed) and avoid granting SYS_ADMIN except in special controlled images.
- For Python apps, set PIP_CACHE_DIR and configure virtualenv to use non-root writable paths to prevent accidental writes to /root during runtime.
- Integrate runtime security observability (Falco rules or eBPF-based monitors) in staging environments to detect syscall anomalies post-deploy; add alerts as part of the CI merge process.
- When crafting AppArmor profiles, prefer file abstractions and path-specific rules instead of blanket 'unconfined' allowances; use the profile to block execution of unexpected binaries in mounted volumes.
- Include a small 'canary' Kubernetes manifest in the repo that runs the hardened image in a non-privileged ServiceAccount pod on every merge to main to catch environment-specific permission assumptions.
- Document the exact commands to reproduce tests (docker run flags, aa-status checks, seccomp validation) in a README so reviewers and new devs can validate runtime security locally.