| Feature | 🦁 Lynx | 🐢 PM2 | 🦖 Supervisor |
|---|---|---|---|
| Runtime | Compiled Go, native | Node.js (V8) | Python (interpreted) |
| Cold start | 7.8 ms | 366 ms | 252 ms |
| Idle RSS | 14.7 MB | 66.7 MB | 27.1 MB |
| RSS w/ 10 procs | 22.8 MB | 69.3 MB | 27.3 MB |
| Daemon binary | 7.2 MB | Node + deps | Python + libs |
| Supervisor | systemd |
Custom daemon | supervisord |
| Crash resilience | Apps outlive the CLI | Apps die with PM2 | Apps die with the daemon |
| Sandboxing | DynamicUser + landlock |
User-space only | User-space only |
| Config | CLI flags or Lynxfile.yml |
ecosystem.config.js |
INI files |
Numbers from
scripts/benchrunning in CI on Ubuntu 24.04 (kernel 6.17). PM2 5.4.3, supervisord 4.2.5. Reproduce locally withdocker build -f scripts/bench/Dockerfile -t lynx-bench . && docker run --rm lynx-bench.
One command spawns an API with no access to /home, no new privileges, and
secrets delivered through systemd credentials instead of environment disk:
lynxpm start api.js \
--name api \
--isolation dynamic \
--env-file .env.productionSecrets never appear in /proc/<pid>/environ, ps, or the on-disk spec.
# Grab the latest .deb from https://github.com/Jaro-c/Lynx/releases
sudo apt install ./lynxpm_*_amd64.deb
sudo usermod -aG lynxadm "$USER" && newgrp lynxadm
sudo systemctl enable --now lynxd
sudo lynxpm install-tools # optional: expose bun/node/go/… to the daemongh release download --repo Jaro-c/Lynx --pattern 'lynxpm_linux_amd64'
install -m 0755 lynxpm_linux_amd64 ~/.local/bin/lynxpmlynxpm start "node server.js" --name api --namespace prod --restart always
lynxpm list
lynxpm logs api --followEvery lifecycle command (stop, restart, reload, reset, delete,
flush) accepts --namespace <ns> or the <ns>:* selector — no more
xargs loops:
lynxpm restart --namespace prod # roll the prod tier
lynxpm stop 'staging:*' # halt everything in staging (quote the glob)
lynxpm delete --namespace old --purge📘 Full docs site: https://jaro-c.github.io/Lynx/ — searchable, with the landing page, quickstart, runtimes, tutorials, and every command's flag reference.
| Topic | Link |
|---|---|
| Runtime recipes — Node / Bun / Python / Go / Rust / Ruby / JVM / … | docs/RUNTIMES.md |
| Tutorials — Next.js, FastAPI, Django, production hardening, Lynxfile | docs/TUTORIALS.md |
Commands reference — start, list, apply, export, … |
docs/commands/ |
| FAQ — "Can I…?" / "Why does X fail?" | docs/FAQ.md |
| Architecture overview | ARCHITECTURE.md |
| Security model + threat model | SECURITY.md |
- System mode (default with the
.deb) — daemon runs as thelynxsystem user undersystemd, socket at/run/lynxd/lynx.sock(0660, grouplynxadm). Does not inherit the caller's env. Use for production. - User mode — daemon runs under
systemd --user, socket at$XDG_RUNTIME_DIR/lynx-<uid>/lynx.sock(0600). Inherits your env. Use for dev.
Launch user mode ad-hoc with lynxd &, or sudo lynxpm startup to
wire the systemd unit at boot. Details in the FAQ.
Anything you can spawn as a Linux process: Node, Bun, Deno, Python
(system / venv / uv / uvx), Go, Rust, Ruby, Java/JVM, PHP, Lua,
Erlang, shell, and more. Per-runtime recipes in
docs/RUNTIMES.md.
| Symptom | Where to look |
|---|---|
cannot reach the Lynx daemon |
lynxd & (user) or sudo systemctl start lynxd (system) |
| Daemon won't start / unit errors | journalctl -u lynxd -f |
--isolation dynamic rejected |
Needs the system-mode daemon (polkit rule is shipped in the .deb) |
| Generic usage / naming / env issues | docs/FAQ.md |
Lynx is Linux-only. Contributors on macOS/Windows should use a
Linux VM or VS Code Remote-WSL — local editors flag false-positive
errors without GOOS=linux.
See CONTRIBUTING.md for the full workflow, and
ARCHITECTURE.md for the internals.
Lynx is open source under the Apache License 2.0 — commercial use, modification, distribution, and the explicit patent grant all included. Preserve the copyright notice and ship a copy of the license with any redistribution.