> ## Documentation Index
> Fetch the complete documentation index at: https://orbit.nckrtl.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# process

> Install, update, start, stop, inspect, and remove systemd services and Docker containers owned by an Instance or a Node, and record Project-owned process definitions.

A Process is one systemd service or Docker container that the Gateway manages for an Instance or a managed Node. The `process` family installs it, changes its desired state, reads its log, and removes it. It also records Project process definitions.

[Processes and schedules](/docs/reference/processes-and-schedules) describes owners, runtimes, environment, presets, locks, and definitions. [Hibernation](/docs/reference/app-dev-runtime-hibernation) describes idle stop and wake for development Processes.

## Choose the owner

`process:create` and `process:list` need exactly one selector. The CLI refuses two selectors, and `--for` without `--project`, before it sends a request.

| Selector | Owner | Result |
| - | - | - |
| `--instance=ID` | Instance | The Gateway derives the Node and the runtime user from the Instance. |
| `--node=ID-or-name` | Managed Node | The Process runs as the Node's managed user. A systemd Process can set `--user`. Shared services such as a Docker database use this owner. |
| `--project=ID --for=production` | Project definition | The Gateway records a definition. Production preparation copies it into a stopped Instance Process. |

`process:show`, `process:update`, and `process:destroy` with `--project` select a definition by name. `process:update`, `process:start`, `process:stop`, `process:restart`, `process:logs`, and `process:destroy` without `--project` take the numeric Process ID from `process:list`. An installed Process update uses that record's owner; it takes no `--instance` or `--node` selector.

## Commands

| Command | Result |
| - | - |
| [`process:create`](#orbit-processcreate) | Install one Process or record one Project process definition. |
| [`process:list`](#orbit-processlist) | List the Processes of one Instance or Node, or the definitions of one Project. |
| [`process:show`](#orbit-processshow) | Show one Project process definition. |
| [`process:update`](#orbit-processupdate) | Replace an installed Process specification in place, or one Project process definition. |
| [`process:start`](#orbit-processstart) | Start one Process and store the running desired state. |
| [`process:stop`](#orbit-processstop) | Stop one Process and store the stopped desired state. |
| [`process:restart`](#orbit-processrestart) | Restart one Process. |
| [`process:logs`](#orbit-processlogs) | Return a log tail, or follow new lines. |
| [`process:destroy`](#orbit-processdestroy) | Remove one Process and its unit or container, or one definition. |

Every command accepts `--json`, and every result includes the request ID. Lists and details show the lifecycle status, failed step, and error code. JSON lists also carry `cpu` and `memory_bytes`, which are null when no sample exists. JSON also carries `user`, null when the Process uses the derived account. Definition lists omit the command. Environment values show as `[REDACTED]`. `process:destroy` asks for confirmation, and the default is No.

## orbit process:create

Install one systemd service or Docker container, stopped or started, or record a Project definition.

```bash theme={"dark"}
orbit process:create <name> (--instance=ID-or-domain | --node=ID-or-name | --project=ID --for=production) [options]
```

| Argument | Required | Meaning |
| - | - | - |
| `name` | yes | At most 63 characters, unique within the owner. |

| Option | Default | Meaning |
| - | - | - |
| `--instance=ID-or-domain` | none | Positive Instance ID or the exact Route domain of a development Instance. |
| `--node=ID-or-name` | none | Node ID or registered Node name. The CLI resolves a name through the Node list. |
| `--user=NAME` | managed user | Systemd account for a Node Process. One letter or underscore, then at most 31 letters, digits, underscores, or hyphens. |
| `--project=ID` | none | Numeric Project ID for a definition. |
| `--for=production` | none | Definition environments. Required with `--project`. Only definitions for `production` are copied. |
| `--preset=PRESET` | none | Configure a development Instance Process. Supported values are `vp-dev`, `annotator`, `agentation-mcp`, and `antigravity-watch`. Refused with `--project`. |
| `--runtime=RUNTIME` | `systemd` | `systemd` or `docker`. |
| `--command=ARG` | none | One argument. Repeat it for each argument, at most 64 of 4,096 bytes each. A systemd command starts with an absolute executable. |
| `--image=IMAGE` | none | Docker image. |
| `--working-directory=PATH` | see [runtimes](/docs/reference/processes-and-schedules#runtimes) | Absolute working directory override. Omit for Docker to keep the image's `WORKDIR`. |
| `--environment=NAME=VALUE` | empty map | Docker environment variable. Repeat as needed; the CLI sends a name-to-value map, not a list. |
| `--port=HOST:CONTAINER[/tcp\|udp]` | none | Docker published port. Repeat as needed. |
| `--volume=SOURCE:TARGET[:ro]` | none | Docker volume. Repeat as needed. |
| `--restart=POLICY` | `never` | `never`, `on-failure`, `always`, or `unless-stopped`. |
| `--keep-alive` | off | Keep the Process running through app-dev idle hibernation. |
| `--start` | off | Start the Process after it installs. Not accepted for a definition. |

A preset owns the runtime, command, working directory, and environment, and refuses those options. It refuses `--keep-alive` for `annotator` and the two Agentation presets. It also refuses `--user`.

`--user` is accepted only with `--node` and a systemd runtime. An Instance, Docker, a preset, or a Project definition returns `process.option_invalid` and sends no request. A name that fails the pattern returns `process.user_invalid` and sends no request. The API field is `user`, and `CreateProcessRequest` takes the same argument. `root` is refused as `process.user_invalid`. The Gateway checks the named account with `getent passwd` over SSH and returns `process.user_unavailable` when it is absent or its home is invalid. The default working directory is that account's home; `--working-directory` overrides it. [Processes and schedules](/docs/reference/processes-and-schedules#owners) describes the option.

```bash theme={"dark"}
orbit process:create assets --instance=commander.test --preset=vp-dev --start
orbit process:create annotator --instance=commander.test --preset=annotator --start
orbit process:create agentation --instance=commander.test --preset=agentation-mcp --start
orbit process:create agentation-watch --instance=commander.test --preset=antigravity-watch --start
```

The `annotator` preset installs the Gateway's annotation server on the Node, runs it through systemd with restart on failure, and publishes `/__orbit/annotator` on the Instance Route. An identical create returns the existing Process and keeps its desired state. Another name for the same preset fails with `process.preset_exists`. [Annotator Process](/docs/reference/agentation#annotator-process) describes its port, private store, environment, and wake behavior.

Install a Vite development server on an Instance and start it:

```bash theme={"dark"}
orbit process:create vite \
  --instance=12 \
  --command=/usr/local/bin/vp \
  --command=run \
  --command=dev \
  --restart=always \
  --start
```

Run a shared Postgres container on a Node:

```bash theme={"dark"}
orbit process:create postgres \
  --node=beast \
  --runtime=docker \
  --image=postgres:18 \
  --port=5432:5432 \
  --volume=postgres-data:/var/lib/postgresql/data
```

Record a queue worker definition that production copies receive:

```bash theme={"dark"}
orbit process:create queue \
  --project=1 \
  --for=production \
  --command=/usr/bin/php \
  --command=artisan \
  --command=queue:work \
  --restart=on-failure
```

A production Process can be installed only after `instance:deploy` selects a release. Starting a Process uses the release selected by `current`; changing `current` does not restart an already running Process.

## orbit process:list

List the Processes of one Instance or Node with their desired and observed states and `keep_alive`, or the definitions of one Project.

```bash theme={"dark"}
orbit process:list (--instance=ID | --node=ID-or-name | --project=ID) [--json]
```

## orbit process:show

Show one Project process definition by name, with its UUID and complete specification.

```bash theme={"dark"}
orbit process:show <name> --project=ID [--json]
```

## orbit process:update

Replace the complete specification of an installed Process, keeping its ID, name, owner, and desired state. A running Process restarts in place; a stopped Process stays stopped. This is not destroy followed by create. It can interrupt a running service and discard a replaced Docker container's writable layer. It does not delete named volumes or require destructive consent. Back up and migrate container-layer data before updating a stateful container; see [Move data before removing an override](/docs/reference/processes-and-schedules#move-data-before-removing-an-override).

```bash theme={"dark"}
orbit process:update <process> [--runtime=systemd|docker] [--command=ARG ...] [--image=IMAGE] [--working-directory=PATH] [--environment=NAME=VALUE ...] [--port=HOST:CONTAINER ...] [--volume=SOURCE:TARGET ...] [--restart=POLICY] [--keep-alive] [--user=NAME] [--json]
```

| Argument or option | Default | Meaning |
| - | - | - |
| `process` | required | Numeric Process ID for an installed Process. |
| `name` | required with `--project` | Definition name in the Project form. |
| `--project=ID` | none | Select the Project-definition form instead of an installed Process. |
| `--for=production` | none | Required only with `--project`; refused for an installed Process. |
| Runtime options | same as create | A complete specification, not a field-by-field merge. `--runtime` defaults to `systemd`, `--restart` to `never`, and `--keep-alive` to off. Supply every setting to retain. |
| `--working-directory=PATH` | owner-derived for systemd; image default for Docker | Omit to remove a prior explicit override. |
| `--environment=NAME=VALUE` | empty map | Repeat for each Docker variable. Replaces the whole map; omit to clear it. Values can contain `=`. |
| `--port`, `--volume` | empty lists | Repeat for each Docker binding or mount to retain. |
| `--user=NAME` | derived account | Accepted only for an installed Node systemd Process. Refused for Project definitions. |
| `--json` | off | One final JSON response, without prompts or progress output. |

The command uses the runtime fields and validation of [`process:create`](#orbit-processcreate). It accepts no `--instance`, `--node`, `--preset`, or `--start`. The runtime options must form a valid complete specification before mutation. A Docker update needs its image and command arguments again. Omitted working directory keeps the image's `WORKDIR`, not `/app`. The Gateway API is `PATCH /api/v1/processes/{process}`; the SDK sends the same fields and environment map. See [Update in place](/docs/reference/processes-and-schedules#update-in-place) for locks, ownership checks, and runtime failures.

Existing Docker Processes keep their stored working directory across upgrades and unchanged convergence. Include an existing `--working-directory=/app` when retaining that override; omitting it is an explicit change to the image default. For example, after verifying that all cache data is in the mounted `/data` volume or completing the linked migration procedure, retain the cache mount and binding while choosing the image default:

```bash theme={"dark"}
orbit process:update 41 \
  --runtime=docker \
  --image=valkey/valkey:8 \
  --command=valkey-server \
  --volume=valkey-data:/data \
  --port=10.44.0.20:6379:6379 \
  --restart=unless-stopped
```

In an interactive terminal, an omitted target uses a finite `Process` selector (`process.update.target`) populated by the read-only Process list, with choices labeled by ID, name, and owner and no default. Selection returns the numeric ID. With `--project`, use a `Process definition` selector populated by that Project's definitions and return the name. An empty list reports `No Processes found.` (or `No Process definitions found.`) and returns `process.id_invalid` (or `process.name_required`) without mutation. A slow lookup shows indeterminate waiting feedback. A missing required Docker image uses a text prompt `Docker image` (`process.update.image`, source `--image`, no default). A missing command collects literal argv items through text prompts (`process.update.command`, source repeated `--command`, no default), never through shell evaluation. Validate each answer and prompt again when it is invalid. Ctrl-C or EOF returns `input.cancelled` before mutation. JSON and noninteractive calls never prompt: an omitted or invalid installed target returns `process.id_invalid` (an omitted definition name returns `process.name_required`), and invalid runtime fields use the create validation codes, including `process.command_invalid`, `process.environment_invalid`, and `process.option_invalid`.

The synchronous Gateway request shows one indeterminate operation with labels `Update Process`, `Updating Process`, and `Updated Process`. It does not invent separate remote stop, install, or start progress stages. Human success uses the Process detail renderer with `ID`, `Name`, owner, runtime, desired state, runtime status, working directory, restart policy, and keep-alive; it never prints environment values. JSON success uses the existing Process response shape with the same ID, lifecycle fields, redacted `runtime_config.environment` map, and request ID. Failure uses the standard failure renderer or JSON `error` envelope, returns nonzero, and prints no success result. Success returns zero. `process.operation_busy` and `process.runtime_lock_failed` retain their existing meanings.

After the Gateway accepts an update, the new specification remains stored even if runtime convergence fails. A lost response or Gateway crash can leave convergence incomplete. Inspect the Process and retry the identical request after the lock is available; equality with the stored specification must not skip stale or unfinished runtime work. An already completed identical retry does not restart an unchanged runtime. If someone has since changed the specification, resolve that newer intent before replaying an old update. See [Persisted intent and retry recovery](/docs/reference/processes-and-schedules#persisted-intent-and-retry-recovery) for the guarantees and limits.

With `--project`, the existing form replaces one Project definition with a complete specification. It requires `--for` and takes the same definition runtime options as create. It keeps the definition UUID and affects later copies only; it does not restart existing Instance copies. Progress labels remain `Update Process definition`, `Updating Process definition`, and `Updated Process definition`, and the response remains the Project definition response.

```bash theme={"dark"}
orbit process:update <name> --project=ID --for=production [--runtime=systemd|docker] [--command=ARG ...] [--image=IMAGE] [--working-directory=PATH] [--environment=NAME=VALUE ...] [--port=HOST:CONTAINER ...] [--volume=SOURCE:TARGET ...] [--restart=POLICY] [--keep-alive] [--json]
orbit process:update queue --project=1 --for=production --command=/usr/bin/php --command=artisan --command=queue:work --restart=on-failure
```

## orbit process:start

Start an installed Process and store the running desired state.

```bash theme={"dark"}
orbit process:start <process> [--json]
```

A competing operation on the same Instance makes the command wait up to 30 seconds and then return `process.operation_busy`. A competing operation on the same Process returns `process.runtime_lock_failed`.

## orbit process:stop

Stop an installed Process and store the stopped desired state. A hibernation wake leaves a stopped Process stopped.

```bash theme={"dark"}
orbit process:stop <process> [--json]
```

## orbit process:restart

Restart an installed Process and store the running desired state.

```bash theme={"dark"}
orbit process:restart <process> [--json]
```

## orbit process:logs

Return one log tail for a Process, or follow new lines as they are written. Docker environment values and credential-shaped data are redacted.

```bash theme={"dark"}
orbit process:logs <process> [--lines=COUNT] [--follow] [--json]
```

| Argument | Required | Meaning |
| - | - | - |
| `process` | yes | Numeric Process ID. |

| Option | Default | Meaning |
| - | - | - |
| `--lines=COUNT` | `100` | Number of lines, from 1 through 1,000. With `--follow`, the number of earlier lines to print first. |
| `--follow` | off | Keep printing new lines until Ctrl-C. |

Without `--follow`, the Gateway reads the tail once over SSH. With `--follow`, the CLI opens a [live log stream](/docs/reference/live-logs): the Node agent reads the log, and the Gateway relays new lines to this CLI only. The CLI prints `[orbit] N lines dropped` or `[orbit] N MiB skipped` where the stream reports them.

When the Gateway cannot open a stream, for example because realtime is not configured or the agent has not joined its log channel yet, the CLI prints a notice on stderr and polls the one-shot read every 5 seconds. It does the same when it cannot connect to Reverb within 15 seconds, with reason `realtime_unreachable`, for example on a host that does not resolve `reverb.orbit`. When a live stream ends because the agent stopped, the source became unreadable, or the Gateway fell behind, the CLI switches to polling in the same way. It prints a part the Gateway sends again, with a `sequence` it already has, only once.

While it polls, the CLI prints only the lines after the last line it printed:

* The first poll reads `--lines` lines. Every later poll reads 1,000 lines, so the last printed line is still in the read even with `--lines=1`.
* When a read does not hold the last printed line, because more than 1,000 lines were written in 5 seconds or the log was rotated, the CLI prints `[orbit] lines may be missing` and then the newest `--lines` lines.
* While a log is shorter than 1,000 lines, the CLI counts new lines by position, so it prints repeated identical lines too. In a longer log it finds its place by the text of the last lines it printed, so a new line identical to those lines can be missed.
* One failed read, for example a 502 while the Gateway restarts, does not end the command. The third failed read in a row does.
* For a systemd Process, the one-shot read and the live stream give each journal line the same text, as `journalctl --output short-iso --utc` prints it, blob data included, so a switch between them neither repeats nor skips a line.
* The live stream cuts a line longer than 8 KiB and ends it with ` [truncated]`, while the one-shot read returns it whole. The CLI counts the two as the same line.
* A one-shot read returns at most 4 MiB of whole lines, so with very long lines the 1,000-line read holds fewer lines. It cuts each journal entry at 256 KiB, as the live stream does.
* For a Docker Process, both reads return standard output and standard error together, in the order the container wrote them.

For the reasons that pass on their own, such as `agent_not_joined` during a `websocket` move, the CLI tries to open a stream again every 30 seconds. A stream it opens after it printed lines asks for 1,000 earlier lines and prints only the ones after the last printed line, or `[orbit] lines may be missing` and the newest `--lines` lines. [Live logs](/docs/reference/live-logs#when-the-live-path-is-not-available) lists the reasons. The CLI prints the notice again only when the reason changes or after a stream ran.

The CLI redacts credential-shaped text before it prints a line and writes `[redacted]`. A value that the agent or the Gateway already redacted shows as `[REDACTED]`, so one line can show both.

With `--json` and `--follow`, the CLI prints one JSON object on each line: `{"type":"lines","lines":[...],"dropped":0,"skipped":0}` for new lines, `{"type":"notice","code":"logs.live_unavailable","reason":"agent_not_joined"}` when it switches to polling, and `{"type":"missing"}` where it prints `[orbit] lines may be missing`. Ctrl-C ends the command with exit code 0 and closes the stream.

```bash theme={"dark"}
orbit process:logs 41 --lines=250
orbit process:logs 41 --follow
```

## orbit process:destroy

Stop and remove the unit or container of one Process, then delete its record. With `--project`, destroy one definition by name.

```bash theme={"dark"}
orbit process:destroy <process> [--project=ID] [--yes] [--json]
```

| Option | Meaning |
| - | - |
| `--project=ID` | Select a definition by name. |
| `--yes` | Confirm without a prompt. A JSON or non-interactive call without `--yes` returns `input.confirmation_required`. A declined prompt, Ctrl-C, or EOF returns `input.cancelled`. |

```bash theme={"dark"}
orbit process:destroy 41 --yes
orbit process:destroy queue --project=1 --yes
```

Destroying a definition leaves every existing Instance copy in place.

## Related

* [`schedule`](/docs/cli/schedule) records recurring commands. Process commands do not operate on Schedules.
* [`instance:destroy`](/docs/cli/instance#orbit-instancedestroy) removes every Process of the Instance. Node Processes stay.
* [`doctor`](/docs/cli/doctor) with `--family=process` compares desired and observed state.
* [`database:server:create`](/docs/cli/database#orbit-databaseservercreate) runs a MySQL server as a Docker Node Process.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.