Introduction
Linux seccomp (secure computing mode) restricts the system calls a process can make. Docker applies a default seccomp profile that blocks approximately 44 syscalls out of 300+ available on modern kernels. When an application inside a container attempts a blocked syscall, it receives EPERM (Operation not permitted), which manifests as cryptic failures that are difficult to diagnose without understanding the seccomp filter.
Symptoms
- Container application crashes with
Operation not permittedon specific operations dmesgshowsaudit: type=1326 seccomp auid=0 uid=0 syscall=XYZ- Docker logs show
fatal: seccomp: operation not permitted - Go applications failing with
clone3: function not implemented - Node.js
fsyncorrseqsyscall blocked on newer kernels
Common Causes
- Default Docker seccomp profile does not include newer syscalls (clone3, rseq, statx)
- Application compiled against newer glibc that uses recently added syscalls
- Custom seccomp profile too restrictive for the workload
- Kernel updated with new syscalls not yet in Docker's default profile
- Container running with
--security-opt seccomp=default.jsonwhich is outdated
Step-by-Step Fix
- 1.Identify the blocked syscall from audit logs:
- 2.```bash
- 3.dmesg | grep seccomp | tail -10
- 4.# Look for the syscall number, e.g., syscall=330
- 5.# Convert to name:
- 6.ausyscall 330
- 7.
` - 8.Check container seccomp profile:
- 9.```bash
- 10.docker inspect <container> | grep -A 5 SeccompProfile
- 11.
` - 12.Test with unconfined seccomp to confirm the issue:
- 13.```bash
- 14.docker run --security-opt seccomp=unconfined <image>
- 15.# If the application works, seccomp is the cause
- 16.
` - 17.Create a custom seccomp profile allowing the needed syscall:
- 18.```bash
- 19.# Start with the default profile
- 20.curl -s https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json > custom-seccomp.json
- 21.# Add the blocked syscall to the allowed list
- 22.python3 -c "
- 23.import json
- 24.with open('custom-seccomp.json') as f:
- 25.profile = json.load(f)
- 26.for action in profile['syscalls']:
- 27.if action['action'] == 'SCMP_ACT_ALLOW':
- 28.action['names'].append('clone3')
- 29.break
- 30.with open('custom-seccomp.json', 'w') as f:
- 31.json.dump(profile, f, indent=2)
- 32."
- 33.
` - 34.Run container with custom profile:
- 35.```bash
- 36.docker run --security-opt seccomp=./custom-seccomp.json <image>
- 37.
` - 38.Alternative: Use a less restrictive built-in profile:
- 39.```bash
- 40.# Docker's unconfined removes all seccomp restrictions
- 41.docker run --security-opt seccomp=unconfined <image>
- 42.# Or disable entirely (not recommended for production)
- 43.docker run --privileged <image>
- 44.
`
Prevention
- Keep Docker updated to get the latest default seccomp profile with new syscall support
- Audit application syscall requirements during development, not production
- Use
strace -con the application outside containers to identify required syscalls - Maintain a curated seccomp profile per application type
- Monitor
dmesgfor seccomp denials in production containers