
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Modern container runtimes expose hundreds of Linux system calls by default, creating a massive attack surface that adversaries exploit for privilege escalation and container escapes. Applying seccomp: Restrict Syscalls for Security is the most effective kernel-level defense to limit this exposure without modifying application code. This guide walks you through building, testing, and enforcing custom seccomp profiles in Docker and Kubernetes environments to achieve genuine least-privilege execution.
--security-opt or Kubernetes Pod Security Standards to prevent exploits while maintaining application functionality.How does seccomp: Restrict Syscalls for Security actually work?
Secure Computing Mode (seccomp) is a Linux kernel facility that intercepts every system call a process attempts to make. When a filter is active, the kernel evaluates the call against a Berkeley Packet Filter (BPF) program before execution. If the syscall matches an allowed rule, it proceeds normally; if not, the kernel returns EPERM, kills the process, or logs the violation depending on the configured action. This happens entirely in kernel space with negligible overhead, making it ideal for high-throughput production workloads.
In practice, you rarely write raw BPF assembly. The OCI Runtime Specification defines a JSON schema that container runtimes like runc and crun translate into BPF automatically. This abstraction lets you declare allowed syscalls, architectures, and actions declaratively. For teams managing Ubuntu server hardening, seccomp complements AppArmor and UFW by operating at a lower abstraction layer—directly controlling what the process can ask the kernel to do.
A common mistake is assuming seccomp replaces other isolation mechanisms. It does not. Seccomp filters syscalls but cannot restrict file paths, network sockets, or capabilities. Use it alongside namespace isolation, capability dropping, and mandatory access control for defense-in-depth. When auditing for SOC 2 or ISO 27001, seccomp provides concrete evidence of least-privilege enforcement at the kernel level, which auditors value highly because it is tamper-resistant and measurable.
How do you create and test custom seccomp profiles?
Start with the default Docker or Kubernetes profile as your baseline. These block approximately 44 dangerous syscalls like unshare, mount, and kexec_load while allowing everything else. For most web applications, the default is sufficient. Custom profiles become necessary when running untrusted code, multi-tenant workloads, or when compliance demands explicit allowlisting.
Generate a baseline profile from actual usage
Rather than guessing which syscalls your application needs, observe it under realistic load. Use strace to capture syscall activity during integration tests or staging traffic:
<!-- Generate syscall list from application execution -->
strace -c -o syscall_stats.txt ./your-application --test-mode
sort -t= -k2 -nr syscall_stats.txt | head -30
<!-- Or capture unique syscalls for profile generation -->
strace -e trace=all -f -o raw_trace.txt ./your-application
grep -oP '^[a-z_]+' raw_trace.txt | sort -u > allowed_syscalls.txt Convert this list into an OCI-compliant JSON profile. The structure requires specifying the default action and explicit allowances:
{
"defaultAction": "SCMP_ACT_ERRNO",
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
"subArchitectures": ["SCMP_ARCH_X86"]
}
],
"syscalls": [
{
"names": ["read", "write", "open", "close", "stat", "fstat"],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["socket", "connect", "bind", "listen", "accept"],
"action": "SCMP_ACT_ALLOW",
"comment": "Network operations for HTTP server"
}
]
} Test safely with logging before enforcement
Never deploy a restrictive profile directly to production. First, set the default action to SCMP_ACT_LOG (kernel ≥5.11) or SCMP_ACT_TRACE to record violations without breaking the application. Monitor logs with journalctl or your structured logging pipeline to identify missing syscalls. Iterate until the application runs cleanly for 24–48 hours under production-like load, then switch to SCMP_ACT_ERRNO.
- Always include architecture specifications—x86_64 and arm64 have different syscall numbers
- Group related syscalls with comments for auditability and maintenance
- Version-control profiles alongside application code in the same repository
- Automate profile validation in CI using
runc specorcrun validate
How do you enforce seccomp profiles in Kubernetes and Docker?
Enforcement mechanisms differ between standalone Docker and orchestrated Kubernetes environments. Both support the OCI JSON format, but the integration points vary significantly.
Docker and containerd integration
Apply profiles per-container using the --security-opt flag. Store profiles in a known directory like /etc/docker/seccomp/ for consistency across hosts:
<!-- Run container with custom seccomp profile -->
docker run --rm \
--security-opt seccomp=/etc/docker/seccomp/webapp.json \
--cap-drop ALL \
myapp:latest
<!-- Verify active profile inside container -->
docker exec <container_id> cat /proc/self/status | grep Seccomp
<!-- Output: Seccomp: 2 (filter mode active) --> For containerd (used by Kubernetes), configure the default profile globally in /etc/containerd/config.toml to ensure all pods inherit baseline restrictions even if developers forget to specify one.
Kubernetes Pod Security Standards and annotations
Kubernetes 1.25+ integrates seccomp through Pod Security Standards (PSS). Set the seccompProfile.type to RuntimeDefault or Localhost in your pod spec. For custom profiles, place JSON files on each node at /var/lib/kubelet/seccomp/profiles/ and reference them by filename:
apiVersion: v1
kind: Pod
metadata:
name: secure-webapp
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/webapp-custom.json
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"] For teams using GitOps with ArgoCD, store profiles in the same repository as your manifests and use a DaemonSet or Ansible playbook to sync them to nodes. This ensures profile versions match application deployments atomically. Never manually copy profiles to nodes—this breaks reproducibility and audit trails.
What are the trade-offs between seccomp, AppArmor, and capabilities?
Understanding where seccomp fits in the security stack prevents misapplication. Each mechanism operates at a different layer and solves distinct problems. Combining them correctly yields compounding security benefits; using the wrong tool creates gaps or operational friction.
| Mechanism | Scope | Granularity | Best For | Limitations |
|---|---|---|---|---|
| seccomp | System calls | Per-syscall, argument-aware | Blocking kernel exploits, container escape prevention | Cannot filter file paths or network addresses |
| AppArmor/SELinux | Resources (files, sockets) | Path-based, label-based | Restricting file access, IPC, device access | Complex policy authoring, distro-specific |
| Capabilities | Root privileges | 38 discrete privilege bits | Dropping specific root powers (NET_RAW, SYS_ADMIN) | Coarse-grained, binary on/off |
| Namespaces | Resource visibility | PID, network, mount, user | Process and network isolation | No syscall filtering, bypassable with CAP_SYS_ADMIN |
In my experience securing fintech infrastructure for Nepal-based companies handling sensitive payment data, the winning combination is: drop all capabilities except those strictly required, apply a restrictive seccomp profile to block unused syscalls, and use AppArmor for path-based file restrictions. This layered approach satisfies both PCI-DSS and local regulatory requirements while keeping the operational burden manageable.
A frequent pitfall is over-restricting seccomp and causing silent failures in background threads or error-handling paths. Applications often use syscalls like epoll_create1 or getrandom only during initialization or failure recovery. Your testing must cover these edge cases comprehensively. When debugging crashes after enabling seccomp, check dmesg for audit messages showing blocked syscalls—these include the exact syscall number and arguments, making profile refinement straightforward.
Implementing seccomp: Restrict Syscalls for Security in Production
Adopting seccomp: Restrict Syscalls for Security transforms your container security posture from passive trust to active enforcement. Start with runtime defaults, measure actual syscall usage in staging, and progressively tighten profiles based on observed behavior. Integrate profile validation into your CI pipeline alongside container image scanning to catch regressions before deployment. Remember that security controls only deliver value when they are maintained—schedule quarterly reviews of seccomp profiles as dependencies and application features evolve. If your team needs help designing or auditing syscall filters for compliance-critical workloads, reach out to discuss your specific requirements.