Prebuilt nsjail executable file packaged as Python wheels with simple Python APIs
Just install and use — no compilation required. python-nsjail provides prebuilt nsjail binaries as Python wheels, making the powerful Linux namespace sandbox immediately available.
- OS: Linux only
- Kernel: Linux 5.10+ (some nsjail features require newer kernel syscalls)
- Permissions: Using nsjail requires CAP_SYS_ADMIN or root
- Python: Python 3.9+
- C++ Runtime: libstdc++ (pre-installed on standard Linux distributions; see Platform Compatibility below)
| Platform | libc | Compatible With | CPU Requirement |
|---|---|---|---|
| manylinux_2_34_x86_64 | glibc 2.34 | Ubuntu 22.04+, Debian 12+, RHEL 9+ | x86-64-v2* (SSE4.2, POPCNT) |
| manylinux_2_34_aarch64 | glibc 2.34 | ARM64 systems | ARM64 (v8+) |
| musllinux_1_2_x86_64 | musl 1.2 | Alpine Linux 3.17+, other musl-based | x86-64-v2* (SSE4.2, POPCNT) |
| musllinux_1_2_aarch64 | musl 1.2 | Alpine Linux ARM64 3.17+ | ARM64 (v8+) |
⚠️ C++ Runtime Requirement:
The nsjail binary is written in C++ and requires libstdc++ \
- manylinux wheels: libstdc++ is pre-installed on all glibc-based distributions (Ubuntu, Debian, RHEL, Fedora, etc.) \
- musllinux wheels: libstdc++ is pre-installed on Alpine 3.17+ \
- Minimal containers (scratch, distroless): you may need to install libstdc++ manually
⚠️ x86-64-v2Note:
The x86_64 wheels are built withmanylinux_2_34containers which usex86-64-v2by default. This requires a CPU from ~2010 or later (supports SSE4.2 and POPCNT instructions). Most modern systems support this. If you need to run on older x86-64 hardware (pre-2010), please use the source distribution or build from source.
pip install python-nsjailNow run:
nsjail --helpYou got nsjail installed!
nsjail --help
nsjail-statusnsjail-status displays installation details including binary location and nsenter availability.
The nsjail command is installed as a console script in your environment's bin/ directory (e.g., .venv/bin, ~/.local/bin, or /usr/local/bin). The underlying binary is bundled with the Python package.
For development, after building the wheel with python setup.py bdist_wheel, the binary will be at src/nsjail/bin/nsjail.
If you need the nsjail binary path in your scripts:
from nsjail import bundled_nsjail
nsjail_path = bundled_nsjail()
print(nsjail_path) # /absolute/path/to/nsjailOr use locate_nsjail() which respects the NSJAIL environment variable and checks system paths:
from nsjail import locate_nsjail
nsjail_path = locate_nsjail() # Returns path with priority: env var > system > bundledPriority:
NSJAILenvironment variable (if set)- System paths:
/usr/local/bin,/usr/bin - Bundled binary (fallback)
The library provides simple functions for creating nsjail subprocesses. Both synchronous and asynchronous APIs are available.
Async API:
import asyncio
import subprocess
from nsjail import async_create_nsjail, NsjailOptions
async def main():
# Basic usage - output to terminal
proc = await async_create_nsjail(
command=["/bin/echo", "hello"],
options=NsjailOptions(chroot="/"),
)
await proc.wait()
# Capture output
proc = await async_create_nsjail(
command=["/bin/cat", "/etc/hostname"],
options=NsjailOptions(chroot="/", user="nobody"),
stdout=subprocess.PIPE,
)
output = await proc.stdout.read()
print(output.decode())
asyncio.run(main())Sync API:
import subprocess
from nsjail import create_nsjail, NsjailOptions
# Capture output synchronously
proc = create_nsjail(
command=["/bin/echo", "hello"],
options=NsjailOptions(chroot="/"),
stdout=subprocess.PIPE,
)
output, _ = proc.communicate()
print(output.decode())Enter an existing container's namespace with async_create_nsenter:
import asyncio
import subprocess
from nsjail import async_create_nsenter
async def main():
# Run 'ip addr' inside container 1234's network namespace
proc = await async_create_nsenter(
target_pid=1234,
namespaces=["net"],
command=["ip", "addr"],
stdout=subprocess.PIPE,
)
output = await proc.stdout.read()
print(output.decode())
asyncio.run(main())For debugging or testing, you can build the nsjail arguments without executing:
from nsjail import build_nsjail_args, NsjailOptions
args = build_nsjail_args(
options=NsjailOptions(chroot="/", user="nobody"),
config_file="/path/to/config.cfg",
)
print("nsjail", *args, "--", "/bin/echo", "hello")
# Output: nsjail --chroot / --user nobody --config /path/to/config.cfg -- /bin/echo helloAll *args and **kwargs are passed directly to the underlying subprocess creation functions:
import subprocess
from nsjail import async_create_nsjail, NsjailOptions
# Pass cwd, env, and other subprocess.Popen / asyncio.create_subprocess_exec arguments
proc = await async_create_nsjail(
command=["/bin/sh", "-c", "echo $FOO"],
options=NsjailOptions(chroot="/"),
stdout=subprocess.PIPE,
cwd="/tmp",
env={"FOO": "value"},
)Configure nsjail with NsjailOptions:
from nsjail import NsjailOptions
options = NsjailOptions(
chroot="/srv/jail", # Chroot directory
user=65534, # Run as user (UID)
group=65534, # Run as group (GID)
hostname="sandbox", # Set hostname
cwd="/tmp", # Working directory inside jail
env={"HOME": "/tmp"}, # Environment variables
bindmount=["/tmp:/tmp"], # Read-write bind mounts
bindmount_ro=["/lib:/lib"], # Read-only bind mounts
tmpfsmount=["/tmp"], # Temporary filesystem mounts
time_limit=60, # Wall time limit in seconds
memory_limit=512, # Memory limit in MB
# ... see NsjailOptions for all options
)NSJAIL - Override the nsjail binary path (for Python API only)
export NSJAIL=/custom/path/to/nsjailSupports ~ and $VAR expansion:
export NSJAIL=~/local/bin/nsjail
export NSJAIL=$XDG_DATA_HOME/nsjail/bin/nsjailExecute untrusted Python code with resource limits:
from nsjail import create_nsjail, NsjailOptions
import subprocess
proc = create_nsjail(
command=["python3", "-c", "print('Hello from sandbox')"],
options=NsjailOptions(
chroot="/srv/jail",
user="nobody",
time_limit=5, # 5 second timeout
memory_limit=128, # 128MB memory limit
bindmount_ro=["/usr/lib", "/usr/lib64"],
),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout, stderr = proc.communicate()
print(stdout.decode())Test network behavior in an isolated environment:
from nsjail import async_create_nsjail, NsjailOptions
async def test_network():
proc = await async_create_nsjail(
command=["curl", "https://example.com"],
options=NsjailOptions(
isolate_network=True, # Disable network
),
)
await proc.wait()
# curl will fail due to network isolationRun tests in a clean environment:
from nsjail import create_nsjail, NsjailOptions
proc = create_nsjail(
command=["pytest", "tests/"],
options=NsjailOptions(
chroot="/tmp/test-env",
bindmount_ro=["/usr", "/lib"],
tmpfsmount=["/tmp"],
),
)async_create_nsjail(command, options=None, config_file=None, *args, **kwargs)- Create async nsjail subprocessasync_create_nsenter(target_pid, namespaces, command, options=None, *args, **kwargs)- Create async nsenter subprocess
create_nsjail(command, options=None, config_file=None, *args, **kwargs)- Create sync nsjail subprocesscreate_nsenter(target_pid, namespaces, command, options=None, *args, **kwargs)- Create sync nsenter subprocess
build_nsjail_args(options=None, config_file=None)- Build nsjail command-line argumentsbuild_nsenter_args(target_pid, namespaces, options=None)- Build nsenter command-line arguments
NsjailOptions- nsjail configuration optionsNsenterOptions- nsenter configuration options
locate_nsjail()- Find nsjail binary (respects NSJAIL env var)bundled_nsjail()- Get bundled nsjail binary path
For development or building from source, see CONTRIBUTING.md.
If you're working on a clone of this repository and want to run/debug nsjail locally, you need to build the wheel first to generate the nsjail binary:
python setup.py bdist_wheel
# or: python -m build --wheel
# or: uv build --wheelThis compiles the nsjail binary and places it at src/nsjail/bin/nsjail, which is required for the package to function in place.
- nsjail: Apache-2.0 (see google/nsjail)
- python-nsjail packaging: Apache-2.0