Skip to content

lynxpm start

Terminal window
lynxpm start <command|file> [flags] [-- <args...>]

Start a new process managed by Lynx. This command creates a new application specification and starts the process via the daemon.

FlagTypeDefaultDescriptionExample
--namestringautoAssign a name to the process.--name my-api
--namespacestringdefaultNamespace for grouping and resolution.--namespace prod
--cwdstringCWDWorking directory for the process.--cwd /var/www
--shellbooleanfalseExecute command inside a shell (/bin/sh -c).--shell
--schedule, --cronstring-Cron schedule for restart (e.g. “@hourly”).--schedule "0 0 * * *"
--restartstringon-failureRestart policy (never, on-failure, always).--restart always
--max-restartsint10Maximum number of restarts before giving up.--max-restarts 5
--restart-delayint2000Delay between restarts in milliseconds.--restart-delay 5000
--backoffstringexpoBackoff strategy (none, linear, expo).--backoff linear
--stop-on-exitlist0Comma-separated exit codes that stop the process.--stop-on-exit 0,143
--log-dirstringautoDirectory for log files (default: system or user local).--log-dir /var/log/my-app
--stdoutstringautoStdout log filename (relative to log-dir).--stdout stdout.log
--stderrstringautoStderr log filename (relative to log-dir).--stderr stderr.log
--log-formatstringplainLog format (plain, json).--log-format json
--log-timestampstringrfc3339Log timestamp (rfc3339, unix, none).--log-timestamp unix
--runtimestring-Runtime for entry file (e.g., node, python).--runtime python3
--env-filestring-Path to a file containing environment variables.--env-file .env
--isolationstringselfIsolation mode (self, dynamic, sandbox).--isolation sandbox
--scale, --instancesint1Number of instances to start.--scale 4
--stop-signalstringSIGTERMSignal on stop (SIGTERM, SIGINT, SIGHUP, SIGQUIT, SIGUSR1, SIGUSR2).--stop-signal SIGINT
--stop-timeoutint10000Grace period before SIGKILL, in ms (1000–300000).--stop-timeout 30000
--memory-maxstringunlimitedHard memory ceiling: 512M, 2G, or raw bytes.--memory-max 512M
--cpu-maxintunlimitedCPU cap as percent of one core (100=1 core, 200=2 cores).--cpu-max 100
--tasks-maxintunlimitedMaximum tasks (threads + subprocesses).--tasks-max 64
-n, --dry-run--Print the resolved spec without starting (rendered as a Spec table; pair with --json for machine-readable output).--dry-run
--jsonbooleanfalseEmit the start result as JSON on stdout ({started, count}). Works with --dry-run too ({spec, scale}).--json
-q, --quiet--Suppress success messages; errors still printed.--quiet
--no-listbooleanfalseSkip the process list printed after the action. The started instances are otherwise highlighted (▸) in the list for easy scanning.--no-list
-h, --help--Show help message.

Any Linux executable works. For language-specific recipes (Node/Bun/Deno, Python with venv / uv / uvx, Go / Rust / Ruby / Java, shell scripts), see docs/RUNTIMES.md.

Start a Node.js script:

Terminal window
lynxpm start main.js

Start with DynamicUser isolation (secure):

Terminal window
lynxpm start main.js --isolation dynamic

Start a scheduled task (runs every hour):

Terminal window
lynxpm start cleanup.sh --schedule "@hourly" --restart never

Success:

Spec saved to /home/user/.config/lynx/apps/my-api.json
Started my-api
ID: e73a9f1b
PID: 12345
Status: online

Error (invalid path):

Error: ERR_BAD_REQUEST: invalid cwd: stat /invalid/path: no such file or directory (BAD_REQUEST)
PolicyDescription
neverNever restart the process, regardless of exit code.
on-failureRestart only if the process exits with a non-zero code (or code not in --stop-on-exit).
alwaysAlways restart the process, even if it exits successfully (code 0).
StrategyDescription
noneNo delay between restarts (immediate).
linearDelay increases linearly: delay * restart_count.
expoDelay increases exponentially: delay * 2^(restart_count-1). Capped at 5 minutes.
OptionValuesDescription
formatplainRaw output as received from the process.
jsonWrap output in JSON structure with metadata.
timestamprfc3339ISO 8601 format (e.g., 2024-01-01T12:00:00Z).
unixUnix timestamp (seconds).
noneNo timestamp added.
ModeDescription
selfRun as the current user (same as lynxd). Default.
dynamicRun as a transient, isolated user via systemd-run. Uses DynamicUser=yes with hardening (NoNewPrivileges, PrivateTmp, ProtectSystem=strict, ProtectHome=yes).

Per-framework patterns (Next.js, FastAPI, Django, Rails, Spring, static sites, cron jobs) live in docs/TUTORIALS.md — runtime-specific invocations (Bun, Deno, venv/uv, compiled Go/Rust, fnm/pyenv/rbenv) live in docs/RUNTIMES.md.

--scale N (alias --instances N) spawns N independent processes. Each one gets a unique ID and name, plus LYNX_INSTANCE=0..N-1 in its env. Lynx does not load-balance — put a reverse proxy (nginx/Caddy/HAProxy) in front of the instances, or use SO_REUSEPORT if your runtime supports it.

// server.js — give each instance its own port
const port = 3000 + Number(process.env.LYNX_INSTANCE ?? 0);

Worked examples (Nginx upstream, --scale with Next.js standalone, live scale up/down) in TUTORIALS.md.

  • Auto-naming: omit --name and Lynx derives <basename>-<shortid>, or <basename>-<index>-<shortid> when --scale > 1.
  • Manual restarts reset the counter: lynxpm restart <id> clears the restart count and backoff timer. --max-restarts only caps the crash loop, not manual operator actions.
  • Visibility: in system mode, processes are visible to anyone in the lynxadm group. In user mode (lynxd &), each user has a private daemon — no cross-user visibility.
  • User mode — the process inherits the full environment of the user running lynxpm start.
  • System mode — the daemon is run by the lynx system user and does not forward its caller’s env (prevents leaking AWS_* / DATABASE_URL / etc.). Whitelisted: PATH, HOME, USER, LOGNAME, SHELL, PWD, LANG, LC_*, TERM, TZ, TMPDIR, XDG_*. Anything else must come from --env-file or AppSpec.Env.
  • Secrets stay off disk: values loaded via --env-file are injected into the process env but not written into the AppSpec JSON in ~/.config/lynx/apps/. No plaintext credentials on-disk in the spec.

  • --shell is gated: accepted in user mode only. System mode refuses it — shell evaluation of an attacker-controlled string against the daemon’s privileges is the exact footgun the hardening model rules out.

  • Isolation picker:

    ModeWorks inTrade-off
    self (default)user + systemZero overhead. No containment — the process inherits the daemon user.
    dynamicsystem onlyStrongest. systemd-run wraps the process: transient UID/GID, ProtectSystem=strict, PrivateTmp, ProtectHome=yes, NoNewPrivileges. Secrets pass via LoadCredential/proc/<pid>/environ shows nothing. Recommended for network-facing prod services.
    sandboxuser + systemUser-namespace + landlock. Blocks writes outside cwd + /tmp. No root or polkit needed.

    In --isolation dynamic --env-file … Lynx stages the file at /var/lib/lynx-pm/creds/<id>/env (0600, daemon-owned) and exposes it via systemd credentials — a small internal wrapper reads the credential and execs your command.