Author: Sukiru (@kojq)
GPU isolation daemon using mount namespace shadowing.
Lullgate uses namespace shadowing as a reliable method for GPU isolation:
| Method | Device State | App Behavior | Overhead |
|---|---|---|---|
| Namespaces | Gone | Handles "not found" gracefully | Zero after launch |
| Cgroups | Visible | Gets "permission denied" → hangs/retry | Ongoing |
| LD_PRELOAD | Bypassable | Statically linked binaries escape | Some |
lullgate detect # Show GPUs
lullgate list # List tracked apps
lullgate set <bin> <w> # Set weight (0-100)
lullgate block <bin> # Set weight=0 (dGPU masked)
lullgate allow <bin> # Set weight=100 (dGPU allowed)
lullgate launch <bin> # Launch with policy
lullgate mode [integrated|hybrid|discrete] # Set or view global system mode
lullgate status # Show detailed power status & active GPU processes
lullgate pm-setup # Setup standard PCI runtime PM autosuspend
lullgate daemon # Run daemon- INTEGRATED: The dGPU is masked for all applications launched via Lullgate.
- HYBRID (Default): Heuristic-based. The dGPU is masked by default, and only allowed for selected apps (with weight ≥ 50).
- DISCRETE: The dGPU is accessible for all applications launched via Lullgate.
To achieve 0.00 Watts idle dGPU power draw on a hybrid GPU system without any kernel hacks, follow these steps:
By default, some Wayland compositors (like cosmic-comp) may start on the NVIDIA dGPU, keeping it active at ~20-30 Watts. Force it to run on the iGPU by setting environment variables in your systemd user session or profile (e.g. ~/.config/environment.d/10-cosmic-gpu.conf or ~/.profile):
COSMIC_RENDER_DEVICE=/dev/dri/renderD129
WLR_DRM_DEVICES=/dev/dri/card1:/dev/dri/card0(Replace renderD129 and card1 with your actual iGPU node paths, which can be found by running lullgate detect.)
Run Lullgate's power setup command:
lullgate pm-setupIf permissions are needed, it will print the exact commands to run with sudo. Specifically, to write the persistent udev rule:
echo 'ACTION=="add|bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", TEST=="power/control", ATTR{power/control}="auto"' | sudo tee /etc/udev/rules.d/90-lullgate-pm.rules
echo 'ACTION=="add|bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", TEST=="power/control", ATTR{power/control}="auto"' | sudo tee -a /etc/udev/rules.d/90-lullgate-pm.rules
sudo udevadm control --reload-rules && sudo udevadm triggerRun the status command:
lullgate statusIf everything is configured correctly and no processes are holding the GPU open, you will see Runtime Status: SUSPENDED (D3cold).
cargo build --releasecargo test- Detection:
GpuDetectorfinds discrete GPUs at startup. - Policy:
Directorlooks up weight in SQLite. - Enforcement:
Sandboxbind-mounts/dev/nullover GPU nodes using mount namespace shadowing (unshare). - Result: App sees dGPU as gone, falls back to iGPU.
- Runtime PM: Once no processes hold the device nodes open, the Linux PCI subsystem automatically suspends the GPU to D3cold (0.00 Watts).
Works on standard kernels (no BPF/LSM boot flags or custom kernel modules required).
MPL 2.0