# Process modes and dispatch

> How the single postgres binary dispatches: postmaster default, --single, --boot, --check, --describe-config, and the pgrust-specific initdb/--initdb entry.

- Repository: malisper/pgrust
- GitHub: https://github.com/malisper/pgrust
- Human docs: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21
- Complete Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/llms-full.txt

## Source Files

- `crates/backend/main/main_main/src/lib.rs`
- `crates/backend/main/main_main/src/help.rs`
- `crates/_support/seam/init/src/bin/postgres.rs`
- `crates/backend/main/initdb/src/lib.rs`
- `crates/backend/postmaster/postmaster/src/main_entry.rs`
- `crates/backend/tcop/postgres/src/guc.rs`

---

---
title: "Process modes and dispatch"
description: "How the single postgres binary dispatches: postmaster default, --single, --boot, --check, --describe-config, and the pgrust-specific initdb/--initdb entry."
---

The `postgres` binary is one process image with several entry modes. The thin shell in `crates/_support/seam/init/src/bin/postgres.rs` builds `TopMemoryContext`, calls `init::init_all()` to register seams, then hands `argv` to `main_main::pg_main`. That function runs shared startup, applies the root check, and either prints a fast-path result, returns `MainOutcome::Initdb`, or dispatches into a never-returning mode owner.

## Binary entry and outcomes

```text
OS argv
  │
  ▼
postgres binary shell
  MemoryContext (TopMemoryContext, leaked 'static)
  init::init_all()  ──► seam + builtin registration
  pg_main(mcx, argv)
  │
  ├── MainOutcome::PrintAndExit(text)  → stdout, exit 0
  ├── MainOutcome::Initdb              → initdb::initdb_main (native only)
  ├── MainOutcome::Dispatched          → mode owner already ran (should not return)
  └── Err(err)                         → stderr "{prog}: {err}", exit 1
```

| Outcome | When | Process exit |
| --- | --- | --- |
| `PrintAndExit` | `--help` / `-?`, `--version` / `-V`, or `--describe-config` after dispatch | `0` after writing text |
| `Initdb` | first arg is `initdb` or `--initdb` | `0` on success; `1` and `initdb: error: …` on failure |
| `Dispatched` | postmaster / single / boot / check (owners diverge) | shell exits `0` if a owner returns (logic error path) |
| `Err` | FATAL startup, including root refusal | `1` |

On `wasm64`, `MainOutcome::Initdb` always exits `1` (no subprocess re-exec). Argv may come from a host import when `std::env::args()` is empty.

## Shared startup before dispatch

`pg_main` always does, in order:

1. Install the ereport panic hook (suppresses backtraces for handled `PGRUST-SQLSTATE` / `PgError` panics unless `PGRUST_PANIC_VERBOSE` is set).
2. Resolve `progname` from `argv[0]`, store it in process globals.
3. `save_ps_display_args`, set `MyProcPid`, `memory_context_init`, `set_stack_base`.
4. Locale setup (`postgres-18` text domain; keep `LC_MONETARY` / `LC_NUMERIC` / `LC_TIME` as `C`; unset `LC_ALL`).
5. Fast-path scan of `argv[1]` for help, version, and initdb (before the root check for help/version).
6. Root check (unless bypassed).
7. Parse must-be-first `--NAME` dispatch options and switch.

Version banner string: `postgres (PostgreSQL) 18.3\n` (`PG_BACKEND_VERSIONSTR`).

## Must-be-first dispatch options

Postgres-style special options are recognized only as `argv[1]` of the form `--name` (two leading dashes). The name after `--` is mapped by `parse_dispatch_option`:

| `argv[1]` | `DispatchOption` | Owner |
| --- | --- | --- |
| *(absent or not a known `--…`)* | `DISPATCH_POSTMASTER` | `postmaster_main` → `PostmasterMain` |
| `--check` | `DISPATCH_CHECK` | `bootstrap::BootstrapModeMain(…, check_only=true)` |
| `--boot` | `DISPATCH_BOOT` | `bootstrap::BootstrapModeMain(…, check_only=false)` |
| `--describe-config` | `DISPATCH_DESCRIBE_CONFIG` | `help_config::GucInfoMain` → `PrintAndExit` |
| `--single` | `DISPATCH_SINGLE` | `postgres_single_user_main` → `PostgresSingleUserMain` |
| `--forkchild…` | never matched | non-`EXEC_BACKEND` only: falls through to postmaster |

Unknown names (including bare `"forkchild"`) yield `DISPATCH_POSTMASTER`. Misplacing a special option later on the line is rejected inside option parsers with `--{name} must be first argument` (single-user / bootstrap / `process_postgres_switches`).

pgrust extension (not a `DispatchOption`): `initdb` or `--initdb` as `argv[1]` returns `MainOutcome::Initdb` before the dispatch switch.

## Root execution policy

`check_root` refuses effective uid 0 and mismatched real/effective uids:

- Root message: `"root" execution of the PostgreSQL server is not permitted…`
- Mismatch: `{progname}: real and effective user IDs must match`

| First argument pattern | Root check |
| --- | --- |
| Default (postmaster, `--single`, `--boot`, `--check`, `initdb` / `--initdb`) | **Enforced** |
| `--help` / `-?` / `--version` / `-V` | Avoided (return before check) |
| `--describe-config` | **Skipped** (`do_check_root = false`) |
| `-C NAME` as first arg (and a second arg present) | **Skipped** (pg_ctl-style GUC print) |

## Mode reference

### Postmaster (default)

**Invocation:** `postgres [OPTION]…` with no special first flag (typically `-D DATADIR` plus listen options and GUCs).

**Entry:** `PostmasterMain(argv) -> !`

**Role:** Multi-process server. Sets `IsPostmasterEnvironment`, creates `PostmasterContext`, resolves install paths, initializes GUCs, selects config / data dir, locks the data directory, checks `global/pg_control`, creates shared memory, opens listen sockets, starts children, enters `ServerLoop`.

**Typical command:**

```bash
postgres -D "$PGDATA" \
  -c listen_addresses=127.0.0.1 -c port=5432 \
  -c io_method=sync -c max_stack_depth=7MB
```

See [Run the server](/run-server) for required stack / `io_method` settings.

### Single-user (`--single`)

**Invocation:** `postgres --single [OPTION]… [DBNAME]` — `--single` **must be first**.

**Entry:** Resolve OS username via `get_user_name_or_exit`, then `PostgresSingleUserMain(argv, username) -> !`.

**Role:** Whole server in one process: standalone process init, GUC tables, `process_postgres_switches` (drops the leading `--single` when parsing), config + data-dir lock (not as postmaster), shared memory, then `PostgresMain` with interactive backend I/O. No postmaster parent (`!IsUnderPostmaster`).

**Defaults:** If no DBNAME is given, the username is used.

**Used by initdb** for post-bootstrap SQL:

```text
postgres --single -F -O -j \
  -c search_path=pg_catalog -c log_checkpoints=false \
  -D <pgdata> template1
```

Help lists: DBNAME, `-d`, `-E`, `-j`, `-r FILENAME`.

### Bootstrap (`--boot`)

**Invocation:** `postgres --boot [OPTION]…` — must be first.

**Entry:** `BootstrapModeMain(mcx, argv, check_only=false)`.

**Role:** Catalog bootstrap from BKI on stdin: standalone process, GUC init, getopt `"B:c:d:D:Fkr:X:-:"`, select config, data-dir lock (non-postmaster), bootstrap processing mode, ignore system indexes, create shmem, `InitProcess` / `BaseInit`, bootstrap XLOG, then bootstrap catalog load. DBNAME is mandatory in bootstrapping mode (help text).

**Used by initdb:**

```text
postgres --boot -F -c log_checkpoints=false -X <wal_seg_bytes> -D <pgdata>
# stdin: substituted postgres.bki
```

### Check (`--check`)

**Invocation:** `postgres --check [OPTION]…` — must be first.

**Entry:** same as boot with `check_only=true`.

**Role:** Run bootstrap startup only through shared-memory and fd-limit setup, then `CheckerModeMain` → `proc_exit(0)`. Used to verify configuration (especially shared-memory sizing) without full bootstrap.

### Describe config (`--describe-config`)

**Invocation:** `postgres --describe-config` (must be first for dispatch; root allowed).

**Entry:** `GucInfoMain()` → tab-separated lines for every visible GUC (name, context, group, type, reset/min/max, short/long desc), returned as `PrintAndExit`.

### Help and version

| Args | Result |
| --- | --- |
| `--help` or `-?` | Full help text from `help(progname)` |
| `--version` or `-V` | `postgres (PostgreSQL) 18.3` |

These return before root check and before mode dispatch.

## pgrust initdb / `--initdb`

Not a PostgreSQL `DispatchOption`. C ships a separate `initdb` binary; pgrust folds the driver into the same `postgres` image.

**Invocation (equivalent):**

```bash
postgres --initdb -D /path/to/pgdata -L /path/to/share
# or
postgres initdb -D /path/to/pgdata -L /path/to/share
```

**Flow:**

```mermaid
flowchart TD
  A["argv[1] is initdb or --initdb"] --> B["MainOutcome::Initdb"]
  B --> C["initdb_main: scaffold PGDATA"]
  C --> D["write config from share samples"]
  D --> E["re-exec self: --boot + BKI stdin"]
  E --> F["re-exec self: --single template1 + SQL stdin"]
  F --> G["Success message + exit 0"]
```

1. Parse options (`-D`/`--pgdata` required; `-U`, `-L`, `-E`, locale flags, `--wal-segsize`).
2. Create subdirectory tree (`global`, `pg_wal`, `base/1`, …), write `PG_VERSION` (`18`).
3. Write configuration files using share samples (`-L` or path derived from the executable).
4. **Bootstrap phase:** spawn `current_exe()` with `--boot …`, feed substituted `postgres.bki` on stdin.
5. **Post-bootstrap phase:** spawn `current_exe()` with `--single … template1`, feed setup SQL on stdin.
6. Print: `Success. You can now start the database server using pgrust/postgres -D <pgdata>`.

Unrecognized initdb flags error as `unrecognized initdb option "…"`. Full flag table: [initdb options reference](/initdb-reference). Cluster layout and phases: [Initialize a cluster](/init-cluster).

## Option parsing after dispatch

Mode-specific parsers share the “must be first” rule via `parse_dispatch_option`:

- **Postmaster / single-user:** `process_postgres_switches` (`getopt` string includes `B:bC:c:D:…` and long options via `-` / `-c`).
- **Boot / check:** bootstrap getopt `"B:c:d:D:Fkr:X:-:"`.
- Misplaced special long options → syntax error, not silent postmaster fallthrough inside those parsers.

Common postmaster flags from `--help` include `-D`, `-c NAME=VALUE`, `--NAME=VALUE`, `-h`, `-k`, `-p`, `-B`, `-N`, `-C NAME` (print GUC and exit when used as first-class postmaster path), and SSL `-l`.

## Dispatch map (owners)

```text
pg_main match dispatch_option
│
├── DISPATCH_CHECK  ──► BootstrapModeMain(check_only=true)  ──► CheckerModeMain → exit 0
├── DISPATCH_BOOT   ──► BootstrapModeMain(check_only=false) ──► BKI bootstrap path
├── DISPATCH_FORKCHILD ──► panic (unreachable without EXEC_BACKEND)
├── DISPATCH_DESCRIBE_CONFIG ──► GucInfoMain → PrintAndExit
├── DISPATCH_SINGLE ──► get_user_name_or_exit → PostgresSingleUserMain → PostgresMain
└── DISPATCH_POSTMASTER ──► PostmasterMain → ServerLoop
```

Seam registration: `main_main::init_seams` installs `parse_dispatch_option`; postmaster / tcop crates install `postmaster_main` and `postgres_single_user_main` during `init::init_all()`.

## Constraints and failure modes

| Condition | Behavior |
| --- | --- |
| Run as root (most modes) | FATAL, exit `1` |
| Special option not first | `--{name} must be first argument` |
| `--forkchild` on this build | Treated as postmaster (not SubPostmasterMain) |
| `initdb` without `-D` | `no data directory specified` |
| Wrong share dir / BKI header | initdb bootstrap fails (header must be `# PostgreSQL 18`) |
| Dispatch owner returns | Shell treats as `Dispatched` and exits `0` (should not happen) |
| FATAL before dispatch | `{prog}: {message}` on stderr, exit `1` |

## Verification signals

```bash
# Fast paths (no data dir)
./target/release/postgres --version
./target/release/postgres --help
./target/release/postgres --describe-config | head

# Cluster create then multi-process server
./target/release/postgres --initdb -D /tmp/pgdata -L "$PGRUST_PGSHAREDIR"
./target/release/postgres -D /tmp/pgdata -c io_method=sync -c max_stack_depth=7MB
```

Expect version line `postgres (PostgreSQL) 18.3`, help starting with `{prog} is the PostgreSQL server.`, and initdb ending with the `Success. You can now start…` line.

## Related pages

<CardGroup>
  <Card title="postgres CLI reference" href="/cli-reference">
    Full flag surface from --help, -c / --NAME=VALUE, and exit paths.
  </Card>
  <Card title="Initialize a cluster" href="/init-cluster">
    --initdb phases, directory layout, and success text.
  </Card>
  <Card title="initdb options reference" href="/initdb-reference">
    Supported and rejected initdb argv.
  </Card>
  <Card title="Run the server" href="/run-server">
    Postmaster startup with required io_method and stack settings.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    PGRUST_PGSHAREDIR, -L, and why wrong share paths break boot.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Root refusal, initdb option gaps, stack overflows, missing share/tz.
  </Card>
</CardGroup>
