Self-hosted service
google/nsjail avatar
google/nsjail

NsJail: Linux process isolation with namespaces, cgroups and seccomp-bpf

A lightweight process isolation tool that utilizes Linux namespaces, cgroups, rlimits and seccomp-bpf syscall filters, leveraging the Kafel BPF language for enhanced security.

4,130 stars379 forksC++Apache-2.0

At a glance

What is it?
NsJail is a C++ sandbox from Google that combines Linux namespaces, resource limits and Kafel seccomp-bpf policies. It is aimed at teams that need to confine untrusted processes on a host they already control, not at people looking for a container runtime.
Who is it for?
Adopt NsJail if you already run Linux workloads that must be confined per process and you are willing to write your own protobuf config or seccomp policy: CTF hosting, fuzzing harnesses and desktop app sandboxing are the documented use cases. Do not adopt it as a replacement for a container runtime with image distribution, orchestration or a Kubernetes API, and do not expect a stable policy language you can copy without reading config.proto.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem NsJail solves, and who it is for

Running an untrusted binary on a Linux host usually means one of two things: a full container runtime, or nothing at all. NsJail sits between them. It is a single C++ binary that takes a command, drops it into new namespaces, applies rlimits and cgroup limits, optionally installs a seccomp-bpf syscall filter, and then execs the target. There is no image format, no daemon and no registry. The README describes it as a "Linux process isolation tool using namespaces, resource limits, and seccomp-bpf syscall filters".

The intended users are visible in the use cases section: CTF challenge hosting, fuzzing, desktop application sandboxing and minimal-environment execution. All four share a trait. The operator controls the host, knows the binary, and wants a per-process boundary rather than a deployable artifact. A CTF organizer who needs each connection to land in a fresh jail on port 8000 has a different problem from a team shipping a microservice, and NsJail is written for the first one.

How the isolation actually composes

The mechanism is a sequence of Linux primitives applied in the child process. Namespaces come first: the README lists UTS, MOUNT, PID, IPC, NET, USER, CGROUPS and TIME, each toggled individually with flags such as --disable_clone_newnet or --enable_clone_newtime. The mount namespace is then populated. NsJail supports chroot() and pivot_root(), read-only bind mounts (-R), read-write bind mounts (-B), tmpfs mounts (-T) and arbitrary mounts (-m SRC:DST:TYPE:OPTS). A custom /proc and /tmp are common, and the example config in the README mounts / read-only, /proc as procfs and /tmp as tmpfs.

Resource limits are applied through rlimits and cgroups. The command line exposes --rlimit_as, --rlimit_cpu, --rlimit_nofile, --time_limit and --oom_score_adj; cgroup integration covers memory, PID, CPU and net_cls, in both v1 and v2, which is why the repository carries separate cgroup.cc and cgroup2.cc files. Finally, if a policy is given, NsJail compiles it with Kafel into a seccomp-bpf filter. The Kafel language is its own layer: you write ALLOW and DEFAULT KILL rules, either inline with --seccomp_string or from a file via --seccomp_policy. The order matters in practice. A policy that omits a syscall the dynamic loader needs will kill the process before main() runs, and that is a configuration error, not a sandbox escape.

Installing NsJail on Ubuntu or Debian and running a first jail

The README gives a source build for Debian and Ubuntu. The dependency list is explicit: autoconf, bison, flex, gcc, g++, git, libprotobuf-dev, libnl-route-3-dev, libtool, make, pkg-config and protobuf-compiler. After cloning, a plain make builds the binary. The Makefile requires pkg-config and errors out if it is missing, and it probes for libnl-route-3.0 with pkg-config, defining HAVE_LIBNL3 when found. If that library is absent, the build still proceeds but networking features that depend on it are compiled out.

bash
sudo apt-get install autoconf bison flex gcc g++ git libprotobuf-dev libnl-route-3-dev libtool make pkg-config protobuf-compiler
git clone https://github.com/google/nsjail.git
cd nsjail
make

The shortest working invocation is the basic isolated shell from the README. It runs once, chroots to /, and drops to UID and GID 99999 before starting bash. If the command starts and you get a shell prompt, the namespace setup and the mount of the root filesystem both succeeded.

bash
./nsjail -Mo --chroot / --user 99999 --group 99999 -- /bin/bash

For a service, the LISTEN mode forks a process per connection. The README's CTF example binds port 8000, chroots into /srv/ctf, drops to 65534, and caps the process at 60 seconds of wall time, 128 MB of address space and 10 seconds of CPU.

bash
./nsjail -Ml --port 8000 \
  --chroot /srv/ctf \
  --user 65534 --group 65534 \
  --time_limit 60 \
  --rlimit_as 128 \
  --rlimit_cpu 10 \
  -- /srv/ctf/challenge

If you would rather not build, the repository ships a Dockerfile that builds on debian:bookworm-slim and copies the resulting nsjail binary to /bin. The README runs it with --privileged, which is required because the tool creates namespaces and mounts.

bash
docker build -t nsjail .
docker run --privileged --rm -it nsjail nsjail --user 99999 --group 99999 --chroot / -- /bin/bash

The privileged requirement, and other cases where NsJail is the wrong tool

The Docker path is the clearest limitation. The README's own command passes --privileged, and that is not incidental: creating a mount namespace, calling pivot_root and applying cgroup limits all need capabilities a default container does not have. If you are already inside a container and want a second layer of isolation, NsJail will not give it to you without loosening the outer container first, which can leave you with less isolation than you started with.

There are two other sharp edges. The first is the seccomp policy. Kafel policies are compiled by NsJail, and the README's example ends with DEFAULT KILL, so any syscall the policy does not name terminates the process. A policy written for a static binary and then applied to a dynamically linked one will fail at startup, and the failure looks like the program crashing rather than the sandbox denying a call. The second is the configuration surface. NsJail accepts either command-line flags or a protobuf config file, and the two overlap heavily. The README shows a config with clone_newnet, clone_newuser, clone_newns, clone_newpid, clone_newipc and clone_newuts, plus uidmap and gidmap blocks and a list of mount blocks. Getting a mount list wrong usually means the target cannot find its own libraries. That is a normal debugging loop, but it is a loop, and there is no dry-run mode described in the README that would let you check a config without launching the process.

NsJail compared with Docker and bubblewrap

Docker and NsJail both use namespaces, but they answer different questions. Docker packages an application into an image, distributes it, and manages its lifecycle across hosts; NsJail takes a path on the local filesystem and a command, and runs it once or per connection. NsJail has no image layer, no registry and no orchestration. If you need to ship a service to a cluster, Docker and its ecosystem are the right layer, and NsJail is at most something you would run inside a container that already grants the needed privileges.

bubblewrap is the closer comparison. It is also a small setuid or user-namespace sandbox that builds a filesystem view and execs a command, and it is used by desktop projects for the same reason NsJail is used for Firefox in the repository's configs. The difference in this project is the depth of the policy layer. NsJail bundles Kafel and exposes seccomp-bpf policy as a first-class input, alongside cgroup v1 and v2 control and per-connection LISTEN mode. bubblewrap leaves syscall filtering largely to the caller. If your threat model includes a process that will actively try syscalls it should not, the built-in Kafel path is the reason to pick NsJail. If you only want to constrain what a well-behaved program can see on disk, the extra policy machinery is overhead you will maintain.

Maintenance, licensing and what an upgrade costs

The repository is not archived, and the last push was on 2026-08-27. Releases are infrequent rather than regular: 3.4 in October 2023, then 3.5 and 3.6 in March 2026. Anyone tracking NsJail should plan around that cadence instead of assuming a steady stream of point releases.

Upgrading is a source build, so the cost is in your build environment, not in a package manager. The Makefile compiles with -std=c++20 and -fno-exceptions, which means a toolchain older than the one the project expects will fail before it reaches any NsJail code. The kafel directory is a submodule, so a fresh clone needs it initialised or the link against kafel/libkafel.a will fail. Config compatibility is the other cost. Because the schema lives in config.proto and the README points there rather than reproducing it, a config that works today is only as stable as that file, and you should diff it against your own copy when you move between releases. The project is licensed Apache-2.0, which permits commercial and closed-source use with the usual notice and patent terms; the bundled Kafel code and any pasta binary you embed may carry their own terms, so check those separately rather than assuming the top-level licence covers everything in the tree.

Editorial conclusion

Adopt NsJail if you already run Linux workloads that must be confined per process and you are willing to write your own protobuf config or seccomp policy: CTF hosting, fuzzing harnesses and desktop app sandboxing are the documented use cases. Do not adopt it as a replacement for a container runtime with image distribution, orchestration or a Kubernetes API, and do not expect a stable policy language you can copy without reading config.proto. Before committing, verify that your kernel exposes the namespaces you plan to clone, that the build links against libprotobuf and libnl-route-3, and that your seccomp policy actually starts the target binary rather than killing it on the first syscall.

Frequently asked questions

How do I install NsJail on Ubuntu?

Install the build dependencies listed in the README (autoconf, bison, flex, gcc, g++, git, libprotobuf-dev, libnl-route-3-dev, libtool, make, pkg-config and protobuf-compiler), clone the repository, then run make. The Makefile requires pkg-config and will stop with an error if it is not installed.

What is NsJail?

It is a Linux process isolation tool from Google that uses namespaces, rlimits, cgroups and seccomp-bpf syscall filters to confine a process. It runs a command either once, per TCP connection, or repeatedly, and it is configured through command-line flags or a protobuf config file.

Can I run NsJail inside Docker?

Yes, and the repository includes a Dockerfile that builds the binary on debian:bookworm-slim. The README's example runs the container with --privileged, because NsJail needs to create namespaces and mounts that a default container does not permit.

What is the difference between NsJail and bubblewrap?

Both build an isolated filesystem view and exec a command, but NsJail bundles the Kafel policy language and exposes seccomp-bpf filtering as a first-class option, along with cgroup v1 and v2 control and a per-connection LISTEN mode. bubblewrap leaves syscall filtering largely to the caller.

Why does my NsJail process die immediately when I use a seccomp policy?

The README's Kafel examples end with DEFAULT KILL, so any syscall the policy does not explicitly allow terminates the process. A policy that omits a call the dynamic loader needs will stop the program before it starts, which looks like a crash rather than a sandbox denial.

Official sources

  1. google/nsjail on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/google-nsjail.svg)](https://hysenlabs.com/projects/google-nsjail)