| scripts | ||
| src | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| TODO.md | ||
ljexec
ljexec is a lightweight and deterministic Linux jail launcher written in C.
It allows unprivileged users to run programs in isolated environments using user namespaces, chroot, and optional OverlayFS layers.
✨ Features
- Rootless by design – no root privileges required at runtime
- OverlayFS support – writable layer over a read-only base
- SquashFS support – use compressed images as base rootfs
- Read-only by default – unless explicitly overridden
- Resource Control via Cgroups - fine-grained CPU and memory limits using cgroupv2
- Minimal dependencies – uses only Linux syscalls and standard C
- Clean mount lifecycle – private mount namespace with automatic teardown
- Compact and modular – well-structured, documented C code
🚀 Usage
ljexec --root /path/to/rootfs [options]
ljexec --image /path/to/rootfs.squashfs [options]
Options
| Option | Description |
|---|---|
--exec CMD |
Command to run inside the jail (default: /bin/sh) |
--root DIR |
Use a directory as the root filesystem (read-only by default) |
--image FILE |
Use a read-only SquashFS image as the base filesystem |
--rw |
Enable writable root filesystem (only with --root) |
--overlay-dir DIR |
Directory to store upper/ and work/ overlay directories |
--mem MBYTES |
Limit the jail's memory usage in megabytes (e.g. --mem 512) |
--cpu PERCENT |
Limit the jail's CPU usage as percentage (e.g. --cpu 50 = half CPU) |
🧩 Rootless Cgroup Limitations
Memory and CPU limits via --mem and --cpu use cgroup v2 and work best if the system allows user-created cgroups.
In most systemd environments, this works if the user slice has +cpu +memory enabled:
sudo sh -c 'echo +cpu +memory > /sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.subtree_control'
If that is not possible or you're outside systemd, you can define an alternative writable cgroup root via:
mkdir -p /tmp/mycg
sudo mount -t cgroup2 none /tmp/mycg
echo "+memory +cpu" | sudo tee /tmp/mycg/cgroup.subtree_control
sudo chown -R $(id -u):$(id -g) /tmp/mycg
sudo chmod -R 770 /tmp/mycg
LJEXEC_CGROOT=/tmp/mycg ./ljexec --mem 512 ...
ℹ️ All cgroup creation and configuration is handled within the mount namespace, and ljexec will log errors but still launch the jail in case of limitations.
📦 Examples
ljexec --root /opt/alpine --exec "/bin/sh"
➡️ Launches an interactive shell inside a read-only root filesystem from the directory /opt/alpine. Since neither --rw nor --overlay-dir is provided, the rootfs is mounted as read-only by default. 📌 Useful for testing a minimal rootfs without making changes.
ljexec --root /opt/alpine --rw --exec "/bin/sh"
➡️ Same as the first example, but the --rw flag makes the root filesystem writable. ⚠️ Changes are persistent and affect the original root directory directly. 📌 Good for when you explicitly want to modify the rootfs.
mkdir -p /tmp/my-overlay
ljexec --root /opt/alpine --overlay-dir /tmp/my-overlay --exec "/bin/sh"
➡️ Creates an OverlayFS mount over /opt/alpine, with upper and work directories under /tmp/my-overlay. This makes the jail filesystem writable without modifying the original rootfs. 📌 Ideal for sessions where persistence is required.
ljexec --image /opt/alpine.squashfs --overlay-dir /tmp/my-overlay --exec "/bin/sh"
➡️ Uses a SquashFS image (/opt/alpine.squashfs) as a compressed read-only base. Overlay is applied from /tmp/my-overlay, making the system writable. 📌 Great for environments with a reproducible image base.
ljexec --image /opt/alpine.squashfs --overlay-dir /tmp/my-overlay --exec "/bin/sh" --mem 512 --cpu 25
➡️ Uses a SquashFS image (/opt/alpine.squashfs) as a compressed read-only base. Overlay is applied from /tmp/my-overlay, making the system writable. The jail is resource-restricted with memory limited to 512 MB and CPU limited to 25%. 📌 Great for predictable production environments with a reproducible image base.
🔐 Security Model
- Uses user namespaces and
chrootfor isolation - No
setuidbinaries or root access required - All mounts are done in a private namespace
- Optional OverlayFS ensures data separation and resettable environments
🛠 Build Instructions
make
./ljexec --help
📁 Project Structure
src/
├── main.c # CLI & entrypoint
├── log.c/.h # Simple logging utilities
├── mount.c/.h # Filesystem mount logic
├── dev.c/.h # /dev setup inside the jail
├── nsmap.c/.h # UID/GID map support for user namespaces
📝 License
MIT License – see LICENSE file for details.
❤️ Why ljexec?
This project was born out of frustration with overly complex container runtimes.
ljexec focuses on clarity, determinism, and simplicity, making it a perfect base for:
- Embedded systems
- Developer sandboxes
- Minimal containerization
- Custom init environments
🌱 Future Enhancement Ideas
These features are not implemented yet but are considered for future versions:
-
Integrated SquashFS Builder
CLI tool to build minimal rootfs images directly from a directory or Dockerfile-like spec. -
Mesh-Based Horizontal Scaling
Peer-to-peer deployment of jails using local mesh discovery and synchronization for clustered workloads. -
Network Isolation
Lightweight per-jail networking viaunshare --net, virtual bridges or user-mode networking with proxying. -
Integration with Runit
Clean service supervision and restart capabilities usingrunitas a native jail manager.
🧭 Project Manifest
ljexec is built from a desire for autonomy, simplicity, and clarity in system tooling. It exists as a direct response to the growing complexity and opacity in container ecosystems.
Core Values
🔹 Minimalism
Only the essentials. No background daemons, no socket activation, no frameworks. Just Linux, chroot, namespaces, and overlay mounts — all under your control.
🔹 Transparency
Every behavior is explicit and predictable. The mount lifecycle, the namespace setup, and the environment are entirely deterministic.
🔹 Rootless by Default
ljexec empowers unprivileged users to isolate processes securely without special system privileges.
🔹 Reproducibility & Resilience
The system should work the same across environments, without requiring network access or external dependencies. Ideal for embedded, air-gapped, or low-trust scenarios.
🔹 Manual-first & Scriptable
Designed to be understood and composed by humans. No hidden state, no orchestration. Write simple scripts and know exactly what happens.
🔹 Host Respect
Your host is not a playground — it deserves clarity. ljexec avoids global side effects, leaves no daemons running, and doesn’t expect control over the system.
Why This Matters
Too many tools today assume control over your system and bury complexity under automation.
ljexec is different — it's not a platform, but a focused tool for those who demand clarity, determinism, and trust in every line of code that runs.