# Processes

Every command accepts a **target**: a numeric id (`0`), a process name (`my-api`), a namespace (`my-namespace`), or `all`. A target that matches a process name (or its cluster instances) always wins; otherwise a namespace target operates on **every process in the group**. Unknown targets are clear errors — `Process or namespace "x" not found — nothing to <verb>` — instead of a silent no-op.

## Namespaces

A namespace is a first-class grouping mechanism, not just metadata. Assign one at start, and every lifecycle verb accepts it as a target:

```bash
pboss start ./web.ts    --name web    --namespace my-app
pboss start ./api.ts    --name api    --namespace my-app
pboss start ./worker.ts --name worker --namespace my-app

pboss restart my-app   # the whole group, in one command
pboss stop my-app      # ✓ Stopped 3 processes in namespace "my-app"
pboss start my-app     # resume every stopped member — atomically
pboss delete my-app    # confirmed first — or --force to skip
```

Group operations report what they touched (a one-line summary above the process table), and the auto-saved dump follows immediately, so the group state survives reboots by default. `pboss list` shows the namespace column.

### Atomic startup ([#31](https://github.com/Procboss/pboss/issues/31))

A namespace is one lifecycle group, so starting it is **atomic**. If any member fails to start, the members **that start invocation brought up** are rolled back — stopped, best-effort, in reverse start order. The original failure stays the primary error; rollback results are reported separately:

```text
namespace "my-app" startup failed: Script not found: ./worker.ts
Rollback: ✓ api stopped, ✓ web stopped
```

The rollback boundary is strictly **invocation-scoped**:

- Members that were **already running** are never rolled back — only what the operation itself started.
- A namespace failure never touches **other namespaces** or **standalone** (namespace-less) processes.
- `pboss start all` applies the same boundaries: standalone processes start independently, every namespace resumes atomically as one unit.
- Namespace `restart` is stop-all + atomic start with the same rollback contract. Namespace `stop`/`reload`/`delete` are best-effort across members — one stubborn member does not leave the rest of the group mid-operation.
- Same-namespace operations are **serialized** — two terminals cannot interleave a `restart` and a `stop` on one group. Different namespaces stay independent.

Standalone behavior is unchanged: a process without a namespace is fully independent, and a failing standalone app never rolls back or blocks the others in an [ecosystem](/cli/ecosystem) start.

### Member-exit policy: `onNsMemberExit`

A namespaced process can decide what to do when **another member of its namespace exits for good** — a terminal stop, or a crash after the restart budget is exhausted. Not a pboss-initiated stop, and not a crash that auto-restart is already handling:

- `ignore` *(default)* — do nothing. Each member keeps running on its own.
- `exit` — stop this process too, so the namespace either runs complete or not at all.

```bash
pboss start ./web.ts --name web --namespace my-app --on-ns-member-exit exit
```

```js
// ecosystem.config.js
module.exports = {
  apps: [
    { name: "web", script: "./web.ts", namespace: "my-app", onNsMemberExit: "exit" },
    { name: "api", script: "./api.ts", namespace: "my-app", onNsMemberExit: "exit" },
  ],
};
```

The policy is per-app and only applies to processes **with** a namespace — standalone processes are never affected by another process's exit. pboss-initiated stops (user stop, rollback, the policy itself) never trigger the policy again, so it cannot cascade. The setting persists in the process dump and survives daemon restarts.

## Dependencies ([#33](https://github.com/Procboss/pboss/issues/33))

Any process can declare what it needs before it can start. pboss resolves the whole dependency graph, starts what it owns in the right order, and checks what the operating system owns:

```js
// ecosystem.config.js
module.exports = {
  apps: [
    { name: "postgres", script: "./postgres-wrapper.ts" },
    { name: "api", script: "./api.ts", dependsOn: ["postgres", "redis"] },
    { name: "worker", script: "./worker.ts", dependsOn: ["api"] },
  ],
};
```

```bash
pboss start worker     # starts postgres → api → worker, in that order
pboss start api.ts --name api --depends-on postgres,redis   # CLI form
```

**Resolution order:** a dependency name resolves against **pboss processes first** (exact names and cluster instances). If nothing matches, pboss asks the system service manager — on Linux, systemd. `postgresql` maps to `postgresql.service`, `mongodb` to `mongod.service`, `redis` to `redis.service`, with common alias differences covered. Only an **active** unit satisfies a dependency.

pboss never starts, stops, or otherwise manages a system service — the check is read-only. Services owned by systemd, another admin, or another orchestrator stay theirs.

### Policies

Every dependency is `"required"` *(default — the dependent cannot start until it is satisfied)* or `"optional"` *(preferred, never blocks)*:

```js
dependsOn: ["postgres", { name: "metrics", policy: "optional" }]
```

The CLI flag accepts `name` or `name:optional` entries, comma-separated: `--depends-on postgres,metrics:optional`.

### Start and restart semantics

- Already-running dependencies are **never restarted** — `pboss start api` (or `restart api`) leaves a running `postgres` alone.
- Resolution is recursive: starting `worker` pulls up `api`, which pulls up `postgres`. Independent dependencies start **concurrently** — the graph is level-ordered, not a flat chain.
- Cycles (`a → b → a`, or a self-reference) are detected **before** anything starts — a clear error, never an infinite loop.
- Rollback is invocation-scoped, same contract as namespaces: if `api` fails, dependencies **this start brought up** are stopped again; processes already running are preserved.
- `restart` pre-flights dependencies **before** stopping the process — a blocked restart fails while the process is still running, instead of stopping it and failing to bring it back.
- Restarting a dependency never restarts its dependents (runtime propagation is a future policy, not a surprise).
- Dependencies can cross namespace boundaries — `web` in namespace `frontend` may depend on `api` in namespace `backend`.

`dependsOn` persists with the process configuration. Boot recovery brings the fleet up **dependencies-first** regardless of the dump's save order, and one failed graph never blocks unrelated graphs.

### Blocked starts say exactly why

```text
Cannot start "api".

Required dependency "postgresql" is unavailable.

Provider: systemd
Service: postgresql.service
State: inactive
```

A name that resolves nowhere reports both misses — `Checked: ProcBoss applications: not found / System services: not found` — and API clients get the same facts as a structured `dependencyFailure` object (see the [programmatic API](/guide/programmatic-api)).

## pboss start

Start a new process or processes.

```bash
pboss start server.ts
```

`pboss start <name|namespace>` — when the positional is not an existing script or config file — **resumes** processes that already exist: every stopped member comes back online, online ones are untouched, nothing new is created. Resuming a **namespace** is atomic — if any member fails to start, only the members this invocation started are rolled back.

```bash
pboss start my-app   # resume every stopped process in the namespace — atomically
pboss start api      # resume one stopped process by name
```

```bash
pboss start server.ts --name api -- --port 8080 --host 0.0.0.0
```

```bash
pboss start server.ts --name api --env NODE_ENV=production --env API_KEY=xxx
```

```bash
pboss start server.ts --name api --max-memory-restart 512M
```

```bash
pboss start server.ts --interpreter "deno run -A" --name deno-api
```

```bash
pboss start server.ts --name api --wait-ready --listen-timeout 10000
```

Arguments after `--` are passed through to your script. The same set of options is available in [ecosystem files](/cli/ecosystem) and the [configuration reference](/guide/config).

### Options

| Flag | Description | Default |
|---|---|---|
| `--name <name>` | Process name | Script filename |
| `--instances <n>` | Number of instances. Use `max` for all CPUs | `1` |
| `--exec-mode <mode>` | `fork` or `cluster` | `fork` |
| `--cwd <path>` | Working directory | Current directory |
| `--env <KEY=VAL>` | Environment variable (repeatable) | — |
| `--interpreter <bin>` | Custom interpreter binary | Auto-detected |
| `--interpreter-args <args>` | Arguments for the interpreter | — |
| `--permissions, --perms <list>` | **Deno only** (runtime-unique — ignored under Bun/Node): comma-separated permission list, e.g. `allow-read,allow-net=api.com,deny-write`; `all` = `-A`, `none` = no permissions | — |
| `--node-args <args>` | Additional runtime arguments | — |
| `--max-memory-restart <size>` | Restart when memory exceeds limit | — |
| `--max-restarts <n>` | Maximum consecutive restarts | `16` |
| `--min-uptime <ms>` | Minimum uptime before a restart is considered stable | `1000` |
| `--restart-delay <ms>` | Delay between restarts | `0` |
| `--kill-timeout <ms>` | Grace period before SIGKILL | `5000` |
| `--no-autorestart` | Disable automatic restart | `false` |
| `--cron <expression>` | Cron expression for scheduled restarts | — |
| `--watch` | Enable file watching | `false` |
| `--ignore-watch <dirs>` | Directories to ignore | `node_modules,.git` |
| `--port <n>` | Base port (auto-incremented in cluster mode) | — |
| `--namespace <ns>` | Process namespace for grouping | — |
| `--on-ns-member-exit <policy>` | Reaction to a namespace sibling's terminal exit: `ignore` or `exit` (namespaced processes only) | `ignore` |
| `--depends-on <list>` | Comma-separated dependencies (issue #33): `name` or `name:optional` each — pboss apps first, then systemd units | — |
| `--wait-ready` | Wait for process ready signal | `false` |
| `--listen-timeout <ms>` | Timeout waiting for ready signal | `3000` |
| `--source-map-support` | Enable source map support | `false` |
| `--merge-logs` | Merge all instance logs into one file | `false` |
| `--log-date-format <fmt>` | Date format prefix for log lines | — |
| `--output <file>` | Custom stdout log path | `~/.pboss/logs/<name>-<id>-out.log` |
| `--error <file>` | Custom stderr log path | `~/.pboss/logs/<name>-<id>-error.log` |
| `--log-max-size <size>` | Max log file size before rotation | `10M` |
| `--log-retain <n>` | Number of rotated log files to keep | `5` |
| `--log-compress` | Gzip rotated log files | `false` |
| `--health-check-url <url>` | HTTP endpoint for health probes | — |
| `--health-check-interval <ms>` | Probe interval | `30000` |
| `--health-check-timeout <ms>` | Probe timeout | `5000` |
| `--health-check-max-fails <n>` | Failures before restart | `3` |
| `--no-daemon`, `-d` | Run in foreground without a daemon (blocks) | `false` |
| `--raw` | Also send child logs to stdout/stderr while retaining log files | `false` |

> **Flags are position-independent.** `--no-daemon` (and all other flags) may appear anywhere relative to the script path:
>
> ```bash
> pboss start --no-daemon app.ts
> pboss start app.ts --no-daemon
> pboss start --name api --no-daemon app.ts --watch
> ```

## pboss stop

Stop a process, all processes with a name, or all processes.

```bash
pboss stop 0
pboss stop my-api
pboss stop my-namespace
pboss stop all
```

A namespace target stops every member of the group (and only those), with a one-line summary naming the group. `all` on an empty list stays a no-op. Group stops are best-effort: a member that refuses to stop does not prevent the others, and the surviving errors are aggregated into one honest report.

Stopping is graceful by default: the process receives `SIGTERM` and has the kill timeout (default 5s) to exit before `SIGKILL` is sent.

Stopping a **dependency** (issue #33) prints a warning naming the processes that still require it — they keep running; only the warning tells you their dependency just went away:

```text
[pboss] warning: stopping "postgres" — still required by "api". They keep running, but their required dependency will be unavailable until it is started again.
```

## pboss restart

Stop and restart a process. The process is fully stopped and then re-spawned — there **will** be downtime. For zero-downtime reloads, use `reload`.

```bash
pboss restart my-api
pboss restart my-namespace
pboss restart all
```

A namespace target restarts every member — including members that were stopped (restart on a stopped process starts it). The restart is **atomic**: stop-all, then an atomic start — if any member fails to come back, the members this restart brought up are rolled back, while members that never stopped (a failed stop) stay running.

Restart is **dependency-aware** (issue #33): required dependencies are pre-flighted *before* the stop, already-running ones are never restarted, and restarting a dependency never restarts its dependents. `pboss restart api` restarts `api` — not the `postgres` under it.

## pboss reload

Graceful zero-downtime reload. New instances start before old ones are killed, ensuring your application always has live workers handling requests.

```bash
pboss reload my-api
pboss reload my-namespace
pboss reload all
```

A namespace reload reloads every member, best-effort — one member failing to reload does not skip the rest of the group. (Atomicity does not apply: graceful reload is zero-downtime by design.)

The reload works per instance:

1. A new process is spawned.
2. pboss waits for it to become stable (or emit a ready signal when `--wait-ready` is enabled).
3. The old process receives `SIGTERM` and the kill timeout to shut down gracefully.
4. The cycle moves to the next instance.

## pboss delete

Stop and remove a process from pboss's management.

```bash
pboss delete 0
pboss delete my-api
pboss delete my-namespace
pboss delete my-namespace --force
pboss delete all
```

Deleting a **namespace** removes every process in the group, best-effort — every member is removed even if one refuses to stop. Because that can take several processes at once, pboss asks for confirmation first — `[y/N]` in a terminal, and a hard refusal with a `--force` hint when stdin is not a TTY (scripts, CI, pipes). Name and cluster deletes keep their old unconfirmed behavior, as does `delete all`.

Deleting a **dependency** (issue #33) is refused when other processes still `dependsOn` it — pboss will not silently break their graph:

```text
Error: Cannot delete "postgres" (postgres) — "api" still depends on it.
Those processes will fail to start until the dependency is restored or removed from their dependsOn.
Run with --force (CLI) or pass { force: true } (API) to delete it anyway.
```

`--force` overrides (it also skips the namespace confirmation above). Members removed **together** with their dependents — a namespace or `all` delete — do not trigger the refusal; nothing is left stranded.

## pboss deps

Inspect the dependency graph (issue #33): direct dependencies with their provider, state, and satisfaction.

```bash
pboss deps api
```

```text
api
├── postgres     [running]    ProcBoss  ✓
├── redis        [running]    ProcBoss  ✓
├── postgresql   [active]     systemd  ✓
└── metrics      [not-found]  systemd  ✗ ~ (optional)
```

`--reverse` answers the other direction — who depends on this process:

```bash
pboss deps postgres --reverse
```

```text
postgres
├── api      [online]
└── worker   [online]
```

Both views are **direct** relationships. A namespace target reports each member; a missing target is a clear error. Resolution runs for the report, but nothing is started — `pboss deps` is pure inspection. The same data is available programmatically via `deps()`.

## pboss scale

Dynamically scale a process group up or down.

```bash
pboss scale my-api 8
pboss scale my-api 2
```

When scaling up, new instances inherit the configuration of existing instances. When scaling down, the highest-numbered instances are stopped and removed first.

## pboss describe

Show detailed information about a process.

```bash
pboss describe my-api
```

```text
┌─────────────────────┬──────────────────────────────────────────┐
│ Name                │ my-api-0                                 │
│ ID                  │ 0                                        │
│ Status              │ online                                   │
│ PID                 │ 4521                                     │
│ Exec Mode           │ cluster                                  │
│ Instances           │ 4                                        │
│ Uptime              │ 2h 15m                                   │
│ Restarts            │ 0                                        │
│ Unstable Restarts   │ 0                                        │
│ CPU                 │ 0.3%                                     │
│ Memory              │ 42.1 MB                                  │
│ File Handles        │ 24                                       │
│ Script              │ /home/user/app/server.ts                 │
│ CWD                 │ /home/user/app                           │
│ Interpreter         │ bun                                      │
│ Watch               │ disabled                                 │
│ Max Memory Restart  │ 512 MB                                   │
│ Health Check        │ http://localhost:3000/health (healthy)    │
│ Cron Restart        │ disabled                                 │
│ Namespace           │ production                               │
│ Created             │ 2025-02-11T10:30:00.000Z                 │
│ Out Log             │ /home/user/.pboss/logs/my-api-0-out.log    │
│ Error Log           │ /home/user/.pboss/logs/my-api-0-error.log  │
└─────────────────────┴──────────────────────────────────────────┘
```

## pboss list

List all managed processes with status, resource usage, and uptime. Supports a **live mode** with auto-refresh and interactive keyboard shortcuts.

```bash
pboss list
pboss list --live
```

Live mode keyboard shortcuts:

| Key | Action |
|---|---|
| `R` | Reload table manually |
| `M` | Sort by memory usage |
| `C` | Sort by CPU usage |
| `U` | Sort by uptime |
| `Q` | Quit live mode |

Live mode refreshes the table every second by default; sorting can be changed on the fly with the shortcuts above.

## pboss signal

Send an OS signal to a process.

```bash
pboss signal my-api SIGUSR2
```

## pboss reset

Reset the restart counter for a process.

```bash
pboss reset my-api
pboss reset all
```
