# pgrust Documentation

> Technical reference for pgrust, a PostgreSQL 18.3-compatible server rewritten in Rust: install and run paths, process modes, configuration constraints, Docker contract, contrib builtins, and regression harnesses.

## Context Links

- [Agent index](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/llms.txt)
- [Human interactive docs](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21)
- [GitHub repository](https://github.com/malisper/pgrust)

## Repository Metadata

- Repository: malisper/pgrust

- Generated: 2026-07-10T05:34:34.017Z
- Updated: 2026-07-10T05:36:29.487Z
- Runtime: Grok CLI
- Format: Documentation
- Pages: 21

## Page Index

- 01. [Overview](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/01-overview.md) - What pgrust exposes as a Postgres 18.3-shaped server binary, who should use it, status limits, and the first docs routes for Docker, source build, and regression verification.
- 02. [Installation](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/02-installation.md) - Prerequisites, system packages, build-time share-dir wiring, and producing the release postgres binary from a clone.
- 03. [Quickstart](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/03-quickstart.md) - Shortest local path: build, --initdb with -L, start with required stack and io_method settings, connect with psql, and verify version() and a trivial query.
- 04. [Docker](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/04-docker.md) - Run malisper/pgrust or a local image build with the official postgres-shaped contract, connect with psql, and confirm first-boot init via entrypoint.
- 05. [Compatibility and status](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/05-compatibility-and-status.md) - Postgres 18.3 behavior and on-disk compatibility, regression-oracle model, non-production status, and extension/PL boundaries that block generic Postgres extensions.
- 06. [Process modes and dispatch](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/06-process-modes-and-dispatch.md) - How the single postgres binary dispatches: postmaster default, --single, --boot, --check, --describe-config, and the pgrust-specific initdb/--initdb entry.
- 07. [Install paths and share directory](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/07-install-paths-and-share-directory.md) - PGRUST_PGSHAREDIR and related build-time path env vars, runtime -L override, vendored share tree contents, and why wrong share/tz paths break boot.
- 08. [Seams and builtin libraries](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/08-seams-and-builtin-libraries.md) - Seam registration model, seams-init aggregation, and dfmgr builtin-library registration that replaces dlopen for ported contrib and regress helpers.
- 09. [Initialize a cluster](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/09-initialize-a-cluster.md) - Create PGDATA with postgres --initdb: supported flags, directory layout, bootstrap and post-bootstrap phases, and success signal text.
- 10. [Run the server](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/10-run-the-server.md) - Start postmaster with -D, socket/TCP flags, mandatory io_method=sync and stack settings, connect with psql, and confirm readiness.
- 11. [Boot from existing Postgres data](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/11-boot-from-existing-postgres-data.md) - Constraints for reusing a Postgres 18.3 PGDATA: control file checks, major-version expectations, and practical recovery when byte order or catalog state does not match.
- 12. [Use contrib extensions](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/12-use-contrib-extensions.md) - Ported contrib modules registered as builtins (citext, hstore, ltree, pg_trgm, pgcrypto, pg_stat_statements, uuid-ossp, and others), CREATE EXTENSION against vendored control/SQL, and what remains unported.
- 13. [Run regression tests](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/13-run-regression-tests.md) - Clone-only scripts/run-regression against vendored PG 18.3 SQL/expected files, psql client requirements, selective test names, and optional isolation/auth/recovery TAP harnesses.
- 14. [postgres CLI reference](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/14-postgres-cli-reference.md) - Flags from --help, must-be-first dispatch options, -c/--NAME=VALUE GUC overrides, and exit paths for --version and --describe-config.
- 15. [initdb options reference](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/15-initdb-options-reference.md) - Supported pgrust initdb argv: -D/--pgdata, -U/--username, -L, -E/--encoding, locale flags, --wal-segsize; space-separated parsing; unsupported options that error.
- 16. [Configuration reference](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/16-configuration-reference.md) - Required and high-impact runtime settings: io_method default vs sync, max_stack_depth, RUST_MIN_STACK, ulimit -s, listen_addresses, and build-time PGRUST_* path env vars.
- 17. [Docker environment reference](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/17-docker-environment-reference.md) - POSTGRES_PASSWORD, POSTGRES_USER, POSTGRES_DB, POSTGRES_INITDB_ARGS, POSTGRES_HOST_AUTH_METHOD, PGDATA, init scripts under /docker-entrypoint-initdb.d, and injected server opts.
- 18. [Test harness environment reference](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/18-test-harness-environment-reference.md) - Environment variables and defaults for run-regression, serial/pg_regress, isolation, auth/ldap TAP, and recovery TAP: PGRUST_BIN, PGRUST_PSQL, PG_SRC, PG_INSTALL, ports, OUTDIR.
- 19. [Build from source](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/19-build-from-source.md) - Host packages, PGRUST_PGSHAREDIR bake-in, cargo build --release --locked --bin postgres, wasm64 single-user notes, and binary location target/release/postgres.
- 20. [Workspace and crate checks](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/20-workspace-and-crate-checks.md) - Cargo workspace scale, fast-check profile, scripts/check-crate and scripts/gate-crate, and how to validate a single ported crate plus seams-init.
- 21. [Troubleshooting](https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/21-troubleshooting.md) - Known failure modes: missing io_method=sync, stack overflows, wrong share/tz path, initdb option gaps, root execution refused, missing psql client, and unported extension loads.

## Source File Index

- `.cargo/config.toml`
- `.dockerignore`
- `Cargo.toml`
- `crates/_support/seam/init/Cargo.toml`
- `crates/_support/seam/init/src/bin/postgres.rs`
- `crates/_support/seam/init/src/lib.rs`
- `crates/_support/seam/seam_core/src/lib.rs`
- `crates/backend/main/initdb/src/lib.rs`
- `crates/backend/main/main_main/src/help.rs`
- `crates/backend/main/main_main/src/lib.rs`
- `crates/backend/postmaster/postmaster/src/gucreads.rs`
- `crates/backend/postmaster/postmaster/src/main_entry.rs`
- `crates/backend/storage/buffer/bufmgr/src/buf_aio.rs`
- `crates/backend/tcop/postgres/src/guc.rs`
- `crates/backend/test_regress/src/lib.rs`
- `crates/backend/utils/fmgr/dfmgr_seams/src/lib.rs`
- `crates/backend/utils/init/miscinit/src/boot_paths.rs`
- `crates/backend/utils/misc/guc_tables/src/consts.rs`
- `crates/backend/utils/misc/guc_tables/src/tables.rs`
- `crates/common/controldata_utils/src/lib.rs`
- `crates/contrib/hstore/src/lib.rs`
- `crates/contrib/pg_stat_statements/src/lib.rs`
- `crates/contrib/pg_trgm/src/lib.rs`
- `crates/contrib/pgcrypto/src/lib.rs`
- `crates/pl/plpgsql/src/handler/src/lib.rs`
- `crates/wasm-libc-shim/src/lib.rs`
- `docker/entrypoint.sh`
- `docker/README.md`
- `Dockerfile`
- `LICENSE`
- `README.md`
- `scripts/cargo-fast-check`
- `scripts/check-crate`
- `scripts/gate-crate`
- `scripts/run-auth-tap`
- `scripts/run-ldap-tap`
- `scripts/run-pg-isolation`
- `scripts/run-pg-regress`
- `scripts/run-pg-regress-serial`
- `scripts/run-recovery-tap`
- `scripts/run-regression`
- `vendor/postgres-18.3/share/extension/hstore.control`
- `vendor/postgres-18.3/share/pg_hba.conf.sample`
- `vendor/postgres-18.3/share/postgres.bki`
- `vendor/postgres-18.3/share/postgresql.conf.sample`

---

## 01. Overview

> What pgrust exposes as a Postgres 18.3-shaped server binary, who should use it, status limits, and the first docs routes for Docker, source build, and regression verification.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/01-overview.md
- Generated: 2026-07-10T05:28:59.138Z

### Source Files

- `README.md`
- `Cargo.toml`
- `LICENSE`
- `crates/_support/seam/init/src/bin/postgres.rs`
- `crates/backend/main/main_main/src/lib.rs`
- `docker/README.md`

---
title: "Overview"
description: "What pgrust exposes as a Postgres 18.3-shaped server binary, who should use it, status limits, and the first docs routes for Docker, source build, and regression verification."
---

pgrust ships a single release binary named `postgres` that presents a PostgreSQL **18.3**-shaped server: the same wire protocol and client contract as stock Postgres, on-disk compatibility with a Postgres 18.3 data directory, and the version banner `postgres (PostgreSQL) 18.3`. The process entry is `crates/_support/seam/init/src/bin/postgres.rs`, which installs seams via `init::init_all()`, then calls `main_main::pg_main` (port of `src/backend/main/main.c`). Catalog bootstrap uses a built-in `initdb` / `--initdb` driver on the same binary rather than a separate C `initdb`. The project is licensed **AGPL-3.0**.

## What you get

| Surface | Behavior |
| --- | --- |
| Binary | `target/release/postgres` from `cargo build --release --locked --bin postgres` (crate `seams-init`) |
| Default mode | Postmaster (no special first argv flag) |
| Cluster create | `postgres --initdb` or `postgres initdb` (pgrust-specific; not a C DispatchOption) |
| Version | `--version` / `-V` → `postgres (PostgreSQL) 18.3` |
| Share data | Vendored tree `vendor/postgres-18.3/share` (or image `/usr/share/postgresql/18`); bake with `PGRUST_PGSHAREDIR`, override at init with `-L` |
| Clients | Any Postgres 18-compatible client (`psql`); the server does not replace `psql` |
| Docker | Official-postgres-shaped image contract (`POSTGRES_*`, `PGDATA`, `/docker-entrypoint-initdb.d`, port 5432) |

```text
  clients (psql / libpq)
           |
           v
  target/release/postgres  (single binary)
     |-- --initdb | initdb  -->  initdb driver (re-exec --boot / --single)
     |-- --single           -->  standalone backend
     |-- --boot | --check   -->  bootstrap mode
     |-- --describe-config  -->  GUC dump + exit
     +-- (default)          -->  postmaster_main
           |
           +-- PGDATA + vendor share (postgres.bki, extension/, timezone/, …)
```

## Who should use it

- **Experimenters and contributors** who want a Postgres-behavior-compatible Rust server they can change from the inside.
- **Compatibility explorers** validating queries against the vendored Postgres 18.3 regression oracle (`scripts/run-regression`, 46k+ queries at launch verification).
- **Docker users** who need a postgres-image-shaped container backed by the Rust binary (`malisper/pgrust` or a local `Dockerfile` build).
- **Not** production operators: the project states it is not production-ready and not performance-optimized.

## Status and limits

<Warning>
pgrust is **not production-ready**. It is **not performance optimized**. Treat it as a research and compatibility surface, not a drop-in production replacement.
</Warning>

| Area | Current boundary |
| --- | --- |
| Behavior oracle | Matches Postgres expected output across more than 46,000 regression queries (launch verification) |
| On-disk | Can boot from an existing Postgres **18.3** data directory (same major / catalog generation assumptions as stock 18.3) |
| I/O | Runtime must use `-c io_method=sync` — sync is the only I/O method this port supports |
| Stack | Raise OS stack (`ulimit -s`) and Rust stack (`RUST_MIN_STACK`); set `-c max_stack_depth=…` (README example: 60000) |
| Extensions | Generic Postgres extensions and procedural languages (PL/Python, PL/Perl, PL/Tcl) are **not** generally compatible |
| Ported contrib | Selected modules registered as **builtins** (no `dlopen`): e.g. `citext`, `hstore`, `ltree`, `pg_trgm`, `pgcrypto`, `pg_stat_statements`, `uuid-ossp`, plus regress helpers |
| Root | Server and initdb refuse root (`check_root`); real and effective UIDs must match |
| Client tools | Regression and Docker healthchecks need a real Postgres 18 `psql`; pgrust does not ship a `psql` replacement |
| wasm | Binary builds for `wasm64` single-user paths; initdb re-exec is native-only |

## Process dispatch (binary argv)

Must-be-first long options (after `--`) map like C `main.c`, plus the pgrust initdb intercept:

| First argument | Outcome |
| --- | --- |
| *(none / ordinary flags)* | `DISPATCH_POSTMASTER` → `postmaster_main` |
| `--single` | Standalone backend |
| `--boot` | Bootstrap mode |
| `--check` | Bootstrap check-only |
| `--describe-config` | Print GUC info and exit (root check skipped) |
| `initdb` or `--initdb` | `MainOutcome::Initdb` → `initdb::initdb_main` (re-execs for `--boot` / `--single`) |
| `--help` / `-?`, `--version` / `-V` | Print and exit 0 before root check |

## First paths

### Docker

Pinned launch image: `malisper/pgrust:v0.1` (`latest` may track the same tag). Local image: `docker build -t pgrust .`.

```bash
docker run -d --name pgrust \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  malisper/pgrust:v0.1

# wait until ready, then:
docker exec -it -e PGPASSWORD=secret pgrust \
  psql -h 127.0.0.1 -U postgres \
  -c "select version(), 1 + 1 as two"
```

The image uses pgrust for both the server and `--initdb` bootstrap; only `psql` remains a C/PGDG client. The entrypoint injects `io_method=sync`, `max_stack_depth`, and a larger stack ulimit without changing the usual `postgres …` CMD contract.

### Build and run from source

Prerequisites (host packages differ by OS; see Installation). Bake the share directory into the binary, then build:

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
  cargo build --release --locked --bin postgres
```

Initialize and start (minimal shape from the repository README):

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres

ulimit -s 65520
RUST_MIN_STACK=33554432 target/release/postgres \
  -D /tmp/pgrust-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000

psql -h /tmp -p 5432 -U postgres -d postgres \
  -c "select version(), 1 + 1 as two"
```

<Check>
Verification signal: `version()` reports PostgreSQL 18.3 and `1 + 1` returns `2`. If the server fails immediately with I/O or stack issues, confirm `io_method=sync` and the stack GUCs/env are set.
</Check>

### Regression verification (clone-only)

```bash
PGRUST_BIN="$PWD/target/release/postgres" \
  scripts/run-regression
```

| Input | Role |
| --- | --- |
| `PGRUST_BIN` | pgrust server (default `target/release/postgres`) |
| `PGRUST_PSQL` | optional path to Postgres 18 `psql` if not on `PATH` |
| `vendor/postgres-18.3/` | share + `regress/sql`, `regress/expected`, `parallel_schedule` |
| Optional args | Selective test names; `test_setup` auto-prepended when missing |

No external PostgreSQL **source tree** or C `initdb` is required. Missing vendored files or a non-executable binary causes an immediate script exit with a short diagnostic.

## Repository layout (operators)

:::files
pgrust/
├── crates/_support/seam/init/   # postgres binary + init_all seam aggregation
├── crates/backend/main/main_main/  # pg_main dispatch (main.c port)
├── crates/contrib/              # ported contrib as builtins
├── vendor/postgres-18.3/        # share + regress + isolation fixtures
├── scripts/run-regression       # clone-only regression driver
├── docker/entrypoint.sh         # official-postgres-shaped entrypoint
└── Dockerfile                   # multi-stage: rustbuild + pgtools + runtime
:::

## Next

<CardGroup>
  <Card title="Docker" href="/docker">
    Run `malisper/pgrust` or a local image with the official postgres-shaped env contract and first-boot init.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    Shortest local path: build, `--initdb` with `-L`, start with stack and `io_method=sync`, connect with `psql`.
  </Card>
  <Card title="Build from source" href="/build-from-source">
    Host packages, `PGRUST_PGSHAREDIR` bake-in, and `cargo build --release --locked --bin postgres`.
  </Card>
  <Card title="Compatibility and status" href="/compatibility">
    Postgres 18.3 behavior and on-disk limits, regression oracle, and extension/PL boundaries.
  </Card>
  <Card title="Run regression tests" href="/regression-tests">
    `scripts/run-regression` against vendored SQL/expected files and optional TAP harnesses.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Common failures: missing `io_method=sync`, stack overflows, wrong share path, root refuse, missing `psql`.
  </Card>
</CardGroup>

---

## 02. Installation

> Prerequisites, system packages, build-time share-dir wiring, and producing the release postgres binary from a clone.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/02-installation.md
- Generated: 2026-07-10T05:29:37.855Z

### Source Files

- `README.md`
- `Cargo.toml`
- `.cargo/config.toml`
- `crates/backend/utils/init/miscinit/src/boot_paths.rs`
- `crates/_support/seam/init/Cargo.toml`
- `vendor/postgres-18.3/share/postgres.bki`

---
title: "Installation"
description: "Prerequisites, system packages, build-time share-dir wiring, and producing the release postgres binary from a clone."
---

pgrust installs as a single native server binary named `postgres`, produced by `cargo build --release --locked --bin postgres` from the workspace entry shell in package `init` (`crates/_support/seam/init`). That binary is both the postmaster and the catalog bootstrap driver (`postgres --initdb`). Compile-time `option_env!("PGRUST_*")` values in `boot_paths` bake install prefixes (especially the share tree used for timezone data and bootstrap templates); the vendored Postgres 18.3 share tree lives at `vendor/postgres-18.3/share`.

<Warning>
pgrust is not production-ready. Prefer Docker or a disposable data directory for experiments. Existing generic Postgres extensions and PL languages are not generally loadable.
</Warning>

## Prerequisites

| Requirement | Detail |
| --- | --- |
| Clone | Full git checkout of [malisper/pgrust](https://github.com/malisper/pgrust) including `vendor/postgres-18.3/share` and `Cargo.lock` |
| Rust | Stable toolchain with Cargo; workspace edition is `2021` (`[workspace.package]` in root `Cargo.toml`) |
| Linker / C toolchain | Host C compiler and linker (Xcode CLT on macOS; `build-essential` on Debian/Ubuntu) |
| pkg-config | Used for OpenSSL, ICU, and libxml2 discovery |
| System libraries | OpenSSL, ICU, OpenLDAP, PAM; default build also links system **libxml2** (`with-libxml`) |
| Client tools (optional for server-only) | Postgres 18 `psql` for interactive use and regression harnesses (`postgresql-client-18` / Homebrew `libpq`) |
| Privileges | Server and init refuse root (`check_root`); build and run as a normal user |

### Host packages

<Tabs>
  <Tab title="macOS (Homebrew)">

```bash
brew install icu4c openssl@3 libpq

export LIBRARY_PATH="$(brew --prefix openssl@3)/lib:${LIBRARY_PATH:-}"
export PKG_CONFIG_PATH="$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix icu4c)/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
export PATH="$(brew --prefix libpq)/bin:$PATH"
```

System libxml2 from the macOS SDK is usually enough for the default `with-libxml` link (`xml2-config` / `pkg-config libxml-2.0`). Override search with `LIBXML2_LIB_DIR` if the linker cannot find `-lxml2`.

  </Tab>
  <Tab title="Debian / Ubuntu">

```bash
sudo apt-get update
sudo apt-get install -y \
  build-essential pkg-config \
  libicu-dev libssl-dev libldap2-dev libpam0g-dev \
  libxml2-dev \
  postgresql-client-18
```

The multi-stage `Dockerfile` rustbuild stage installs `build-essential`, `pkg-config`, `libicu-dev`, `libldap2-dev`, `libpam0g-dev`, and `libssl-dev`, then runs the same release cargo build. Runtime images also need `libicu*`, `libxml2`, `libssl3`, `libldap*`, and `libpam0g`.

  </Tab>
</Tabs>

### What the binary links

The release `postgres` binary is a large workspace product (~1,400 crates). Native FFI surfaces that require host packages include:

- **OpenSSL** — TLS (`be_secure_openssl` stack)
- **ICU** — collation provider (`pg_locale_icu`)
- **OpenLDAP / PAM** — auth FFI crates
- **libxml2** — default `with-libxml` on `xml_libxml_ffi` (`pkg-config libxml-2.0` or `xml2-config`, else `LIBXML2_LIB_DIR`)

Wasm64 single-user builds are a separate target (nightly `build-std`, special profiles); they are not the primary host install path. See [Build from source](/build-from-source) for wasm notes.

## Build-time share directory wiring

Install prefixes are compile-time constants, not runtime env vars for the running server’s default share lookup.

### How bake-in works

`crates/backend/utils/init/miscinit/src/boot_paths.rs` resolves directories via `option_env!("PGRUST_…")`, falling back to PostgreSQL-style `/usr/local/pgsql/…` defaults when unset:

| Build env var | Default if unset in source |
| --- | --- |
| `PGRUST_PGSHAREDIR` | `/usr/local/pgsql/share` |
| `PGRUST_PGBINDIR` | `/usr/local/pgsql/bin` |
| `PGRUST_PKGLIBDIR` | `/usr/local/pgsql/lib` |
| `PGRUST_SYSCONFDIR` | `/usr/local/pgsql/etc` |
| `PGRUST_INCLUDEDIR` / `PGRUST_PKGINCLUDEDIR` / `PGRUST_INCLUDEDIRSERVER` | under `/usr/local/pgsql/include…` |
| `PGRUST_LIBDIR` | `/usr/local/pgsql/lib` |
| `PGRUST_LOCALEDIR` / `PGRUST_DOCDIR` / `PGRUST_HTMLDIR` / `PGRUST_MANDIR` | under `/usr/local/pgsql/share…` |

**`PGRUST_PGSHAREDIR` is the critical one.** Timezone (`timezone/`, `timezonesets/`), text search data, and path relativization for share assets are derived from the baked share path. A wrong bake-in produces boot failures that look like missing tz or template files.

### Workspace Cargo default

`.cargo/config.toml` sets:

```toml
[env]
CARGO_INCREMENTAL = "0"
PGRUST_PGSHAREDIR = "/tmp/pgrust_share"
```

`force` is left false: these apply only when the variable is **not** already in the process environment. Explicit:

```bash
PGRUST_PGSHAREDIR=/somewhere cargo build …
```

still overrides the config default.

<Note>
A plain `cargo build` without `PGRUST_PGSHAREDIR` bakes `/tmp/pgrust_share`. That directory must contain a full share tree (or you must override to the vendored path). The README build recipe sets the env to the vendored tree for a clone-local install.
</Note>

### Recommended bake-in for a clone

Point compile-time share at the vendored tree:

```bash
export PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share"
```

Docker builds set `ENV PGRUST_PGSHAREDIR=/usr/share/postgresql/18` so the image binary matches the PGDG share path copied into the runtime stage.

### Runtime `-L` vs bake-in

| Mechanism | When | Effect |
| --- | --- | --- |
| `PGRUST_PGSHAREDIR` at **cargo build** | Compile | Baked into binary; used by `get_share_path` for tz and default share resolution |
| `postgres --initdb -L <dir>` | Initdb only | Overrides the share directory for bootstrap templates (`postgres.bki`, `system_*.sql`, samples, etc.) |

Initdb without `-L` uses `get_share_path` from the baked configuration. Best practice: bake the vendored (or installed) share path **and** pass the same path as `-L` on first initdb.

### Vendored share tree

`vendor/postgres-18.3/share` is the in-repo Postgres 18.3 share payload. Required bootstrap inputs include:

:::files
vendor/postgres-18.3/share/
├── postgres.bki
├── system_constraints.sql
├── system_functions.sql
├── system_views.sql
├── information_schema.sql
├── sql_features.txt
├── snowball_create.sql
├── postgresql.conf.sample
├── pg_hba.conf.sample
├── pg_ident.conf.sample
├── extension/          # control + SQL for ported contrib
├── timezone/
├── timezonesets/
└── tsearch_data/
:::

`postgres.bki` begins with `# PostgreSQL 18` and drives catalog bootstrap. Extension control/SQL under `extension/` is used by `CREATE EXTENSION` for ported builtins.

## Build the release binary

<Steps>
  <Step title="Install host packages and Rust">
    Install the OS packages above and a recent stable Rust toolchain with Cargo. Keep `Cargo.lock` from the clone; release builds use `--locked`.
  </Step>
  <Step title="Set share-dir env for this build">
    From the repository root:

```bash
export PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share"
```

    Confirm the tree exists: `test -f "$PGRUST_PGSHAREDIR/postgres.bki"`.
  </Step>
  <Step title="Compile postgres">

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
  cargo build --release --locked --bin postgres
```

    Equivalent Dockerfile command (with image-local `PGRUST_PGSHAREDIR` already in `ENV`):

```bash
cargo build --release --locked --bin postgres
```

    The binary target is defined on package `init`:

```toml
[[bin]]
name = "postgres"
path = "src/bin/postgres.rs"
```

    That entry shell creates the top memory context, calls `init::init_all` (seam + builtin registration across ported crates), then dispatches through `main_main::pg_main`.
  </Step>
  <Step title="Locate the artifact">
    Output path:

```text
target/release/postgres
```

    (`CARGO_TARGET_DIR` relocates this; Docker uses `/build/target` then copies to `/opt/pgrust-postgres`.)
  </Step>
</Steps>

### Release profile notes

Root `Cargo.toml` release profile uses `debug = 0`, `strip = "symbols"`, `lto = false`, `codegen-units = 1`. Builds are large (full server workspace). Incremental is disabled by default via `.cargo/config.toml` (`CARGO_INCREMENTAL = "0"`).

## Verify the install

After a successful build:

```bash
./target/release/postgres --version
# or
./target/release/postgres -V
```

Expected banner (from `PG_BACKEND_VERSIONSTR`):

```text
postgres (PostgreSQL) 18.3
```

Sanity checks before creating a cluster:

| Check | Command / condition |
| --- | --- |
| Binary exists and is executable | `test -x target/release/postgres` |
| Share bake-in matches tree | Built with `PGRUST_PGSHAREDIR` pointing at a dir that has `postgres.bki` and `timezone/` |
| Client on PATH (for later connect) | `psql --version` (Postgres 18 client recommended) |
| Not root | `id -u` ≠ 0 for server start |

Initialize a cluster only after verification (see [Initialize a cluster](/init-cluster)):

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

Success ends with text of the form: `Success. You can now start the database server using pgrust/postgres -D …`.

## Install without building: Docker

If you only need a running instance and not a local binary:

```bash
docker run -d --name pgrust \
  -e POSTGRES_PASSWORD=secret \
  malisper/pgrust:v0.1
```

`malisper/pgrust:latest` tracks the same launch line; pin `v0.1` for reproducibility. Local image:

```bash
docker build -t pgrust .
docker run --rm -e POSTGRES_PASSWORD=secret -p 5432:5432 pgrust
```

The image builds the same release binary, bakes `PGRUST_PGSHAREDIR` to `/usr/share/postgresql/18`, ships PGDG share + `psql`, and uses an official-postgres-shaped entrypoint. Full env contract: [Docker](/docker) and [Docker environment reference](/docker-reference).

## Outputs and layout after a source install

| Artifact | Path |
| --- | --- |
| Server / initdb binary | `target/release/postgres` |
| Vendored share (source of `-L` and recommended bake-in) | `vendor/postgres-18.3/share` |
| Workspace lockfile | `Cargo.lock` (required with `--locked`) |
| Optional smoke default share | `/tmp/pgrust_share` (`.cargo` default; not populated by cargo itself) |

There is no separate `make install` prefix into `/usr/local`. Running from the tree (or copying the binary plus a share tree whose absolute path matches the bake-in) is the supported model. Changing `PGRUST_PGSHAREDIR` requires a **rebuild**.

```text
  clone
    │
    ├─ cargo env: PGRUST_PGSHAREDIR ──► baked into target/release/postgres
    │
    ├─ vendor/postgres-18.3/share ────► -L at --initdb (templates / BKI)
    │                              └──► tz / timezonesets if path matches bake-in
    │
    └─ host libssl, libicu, libxml2, ldap, pam ──► link of postgres
```

## Install-time failure modes

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Link errors for `ssl`, `icu`, `ldap`, `pam`, `xml2` | Missing dev packages or `PKG_CONFIG_PATH` | Install packages; set Homebrew prefixes on macOS; set `LIBXML2_LIB_DIR` if needed |
| `cargo build` fails resolving lock | Dirty tree / wrong Rust / out-of-sync lock | Use `--locked` on the committed `Cargo.lock`; update toolchain |
| Initdb / boot cannot open `postgres.bki` or tz files | Wrong bake-in or missing `-L` | Rebuild with `PGRUST_PGSHAREDIR` → vendored share; pass same path as `-L` |
| Binary “works” but timezone sets missing | Baked `/tmp/pgrust_share` empty | Populate that tree or rebuild with vendored path |
| `"root" execution of the PostgreSQL server is not permitted` | euid 0 | Run as non-root |
| `psql: command not found` | No client package | Install `postgresql-client-18` / Homebrew `libpq` |
| Unrecognized initdb option | Flag not implemented in pgrust initdb | Use only supported argv (see [initdb options reference](/initdb-reference)) |

After install, server start still requires runtime settings such as `io_method=sync` and elevated stack limits; those are not install steps. See [Run the server](/run-server) and [Troubleshooting](/troubleshooting).

## Related pages

<CardGroup>
  <Card title="Quickstart" href="/quickstart">
    Build, initdb with -L, start with required GUCs, connect with psql.
  </Card>
  <Card title="Build from source" href="/build-from-source">
    Deeper host-package, bake-in, and wasm64 single-user notes.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    Full PGRUST_* path model, -L override, and share tree contents.
  </Card>
  <Card title="Docker" href="/docker">
    Prebuilt image or local docker build without a host cargo install.
  </Card>
  <Card title="Initialize a cluster" href="/init-cluster">
    postgres --initdb flags, layout, and success signal.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Runtime GUCs and build-time path env vars.
  </Card>
</CardGroup>

---

## 03. Quickstart

> Shortest local path: build, --initdb with -L, start with required stack and io_method settings, connect with psql, and verify version() and a trivial query.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/03-quickstart.md
- Generated: 2026-07-10T05:29:23.144Z

### Source Files

- `README.md`
- `crates/backend/main/initdb/src/lib.rs`
- `crates/_support/seam/init/src/bin/postgres.rs`
- `scripts/run-regression`
- `crates/backend/utils/misc/guc_tables/src/tables.rs`

---
title: "Quickstart"
description: "Shortest local path: build, --initdb with -L, start with required stack and io_method settings, connect with psql, and verify version() and a trivial query."
---

The `postgres` binary (`crates/_support/seam/init`, `[[bin]] name = "postgres"`) is the single entry shell for postmaster, bootstrap, single-user, and pgrust’s own cluster init. After a release build with `PGRUST_PGSHAREDIR` pointed at the vendored share tree, create `PGDATA` with `postgres --initdb -L …`, start postmaster with `io_method=sync` plus stack overrides, then verify with a Postgres 18 `psql` client.

## Prerequisites

| Requirement | Role |
| --- | --- |
| Rust toolchain + `cargo` | Builds `target/release/postgres` |
| Clone with `vendor/postgres-18.3/share` | Supplies `postgres.bki`, system SQL, config samples, timezone data for `-L` / baked share |
| Host libs (OpenSSL, ICU, …) | Link-time deps for the release binary |
| Postgres 18 `psql` | Only external client; pgrust does not ship a `psql` replacement |
| Non-root OS user | Server and `--initdb` refuse root (`check_root`) |

### Host packages

<Tabs>
  <Tab title="macOS">
```bash
brew install icu4c openssl@3 libpq

export LIBRARY_PATH="$(brew --prefix openssl@3)/lib:${LIBRARY_PATH:-}"
export PKG_CONFIG_PATH="$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix icu4c)/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
export PATH="$(brew --prefix libpq)/bin:$PATH"
```
  </Tab>
  <Tab title="Debian/Ubuntu">
```bash
sudo apt-get update
sudo apt-get install -y build-essential pkg-config libicu-dev libssl-dev \
  libldap2-dev libpam0g-dev postgresql-client-18
```
  </Tab>
</Tabs>

<Note>
If `psql` is not on `PATH`, set `PGRUST_PSQL=/path/to/psql` for scripts, or pass the full path when connecting.
</Note>

## Build

Bake the vendored share path at compile time, then produce the release binary:

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
cargo build --release --locked --bin postgres
```

| Output | Path |
| --- | --- |
| Server binary | `target/release/postgres` |
| Share tree for `-L` | `vendor/postgres-18.3/share` |

`PGRUST_PGSHAREDIR` is compiled into path helpers (`option_env!("PGRUST_PGSHAREDIR")`, default fallback `/usr/local/pgsql/share`). Runtime init still needs an explicit `-L` when the executable is not under a classic install layout.

Optional version check (must-be-first style flag):

```bash
target/release/postgres --version
# postgres (PostgreSQL) 18.3
```

## Initialize a cluster

`--initdb` / `initdb` is intercepted in `pg_main` before normal dispatch and handed to the ported initdb driver. The driver scaffolds `PGDATA`, writes config from share samples, then re-execs the same binary for `--boot` and `--single`.

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

### Flags used here

| Flag | Meaning |
| --- | --- |
| `-D` / `--pgdata` | Data directory (required) |
| `-L` | Share directory override (must contain `postgres.bki` and related files) |
| `-U` / `--username` | Bootstrap superuser (default: `$USER` or `postgres`) |
| `-E` / `--encoding` | Server encoding; `UTF8` maps to encoding id 6 |
| `--no-locale` | Forces `C` collate/ctype |

<Warning>
initdb argv is **space-separated** (`--opt value`). Unsupported options (for example `-A`, `--data-checksums`, `--pwfile`) fail with `unrecognized initdb option`.
</Warning>

### Success signal

On success, stderr ends with:

```text
Success. You can now start the database server using pgrust/postgres -D /tmp/pgrust-data
```

Phases printed along the way: configuration files → bootstrap script → post-bootstrap initialization.

## Start the server

pgrust requires explicit I/O and stack settings that differ from a stock C Postgres boot.

```bash
ulimit -s 65520

RUST_MIN_STACK=33554432 target/release/postgres \
  -D /tmp/pgrust-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000
```

### Required runtime settings

| Setting | Value used in quickstart / harnesses | Why |
| --- | --- | --- |
| `io_method` | `sync` | GUC boot default is `worker` (`IOMETHOD_WORKER`). Docker, README, and `scripts/run-regression` all force `sync` for a working boot path. |
| `max_stack_depth` | `60000` (kB) | Boot default is raised to `2048` kB vs C’s `100`; regress/harness still pass a much higher override for deep catalog work. |
| `RUST_MIN_STACK` | `33554432` | Rust thread stack floor; default OS stack is too small. |
| `ulimit -s` | `65520` | Process stack size; Docker entrypoint applies the same. |

### Socket-only local flags

| Flag | Effect in this recipe |
| --- | --- |
| `-D` | Data directory from `--initdb` |
| `-F` | Used by the regression harness start line (fsync off for local smoke) |
| `-c listen_addresses=` | Empty list: no TCP listen; Unix socket only |
| `-k /tmp` | Unix socket directory (keep short on macOS `sun_path` limits) |
| `-p 5432` | Port for the socket |

Docker’s entrypoint injects the same `io_method=sync` and `max_stack_depth=60000` (plus image-specific `unix_socket_directories`) on every server start.

## Connect and verify

In another terminal, use a Postgres 18 client against the socket:

```bash
psql -h /tmp -p 5432 -U postgres -d postgres \
  -c "select version(), 1 + 1 as two"
```

### Expected signals

| Check | Pass condition |
| --- | --- |
| Ready | `psql` connects without “connection refused” / “no such file” |
| Version | `version()` reports a PostgreSQL 18.3-shaped banner (binary `--version` is `postgres (PostgreSQL) 18.3`) |
| Query | `two` column is `2` |

Readiness probe used by `scripts/run-regression`:

```bash
psql -h /tmp -p 5432 -U postgres -d postgres -c 'SELECT 1'
```

## End-to-end path (compact)

```text
vendor/postgres-18.3/share  ──-L / PGRUST_PGSHAREDIR──►  postgres --initdb -D PGDATA
                                                              │
                                                              ▼
                         RUST_MIN_STACK + ulimit -s           PGDATA
                         -c io_method=sync
                         -c max_stack_depth=60000
                                                              │
                                                              ▼
                         psql -h <sockdir> -p <port>  ──►  version() / SELECT 1
```

## Common failures

| Symptom | Fix |
| --- | --- |
| Boot fails without `io_method=sync` | Pass `-c io_method=sync` (default is `worker`) |
| Stack overflow / “stack depth limit exceeded” | Raise `ulimit -s`, `RUST_MIN_STACK`, and `-c max_stack_depth=…` |
| initdb cannot find bootstrap files | Pass `-L "$PWD/vendor/postgres-18.3/share"`; rebuild with `PGRUST_PGSHAREDIR` if timezone/share resolution is wrong |
| `unrecognized initdb option` | Drop unsupported C-initdb flags; use only the supported subset |
| `"root" execution of the PostgreSQL server is not permitted` | Run as a non-root user |
| `no psql client found` | Install `libpq` / `postgresql-client-18` or set `PGRUST_PSQL` |

## Docker alternative

If you only need a containerized smoke test (not a source build):

```bash
docker run -d --name pgrust -e POSTGRES_PASSWORD=secret malisper/pgrust:v0.1
# wait for readiness, then:
docker exec -it -e PGPASSWORD=secret pgrust psql -h 127.0.0.1 -U postgres
```

The image entrypoint runs pgrust `--initdb` with `-L` and always injects the stack / `io_method` server opts.

## Optional: regression smoke after local bring-up

With the same binary and a Postgres 18 `psql` on `PATH`:

```bash
PGRUST_BIN="$PWD/target/release/postgres" \
scripts/run-regression
```

The script re-runs `--initdb` against the vendored share, starts postmaster with the same `io_method=sync` / stack pattern, and compares SQL output to vendored expected files.

## Next

<CardGroup>
  <Card title="Build from source" href="/build-from-source">
    Host packages, `PGRUST_PGSHAREDIR` bake-in, release binary location, and wasm notes.
  </Card>
  <Card title="Initialize a cluster" href="/init-cluster">
    Full `--initdb` phases, directory layout, and success text.
  </Card>
  <Card title="Run the server" href="/run-server">
    Postmaster flags, readiness, and connection details beyond the minimal recipe.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `io_method`, stack GUCs/env, listen/socket settings, and build-time `PGRUST_*` paths.
  </Card>
  <Card title="Docker" href="/docker">
    Official image contract and first-boot entrypoint behavior.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Known failure modes for stack, share paths, initdb gaps, root, and clients.
  </Card>
</CardGroup>

---

## 04. Docker

> Run malisper/pgrust or a local image build with the official postgres-shaped contract, connect with psql, and confirm first-boot init via entrypoint.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/04-docker.md
- Generated: 2026-07-10T05:29:07.462Z

### Source Files

- `README.md`
- `Dockerfile`
- `docker/entrypoint.sh`
- `docker/README.md`
- `.dockerignore`

---
title: "Docker"
description: "Run malisper/pgrust or a local image build with the official postgres-shaped contract, connect with psql, and confirm first-boot init via entrypoint."
---

The root `Dockerfile` and `docker/entrypoint.sh` ship a **Postgres-shaped container**: same env vars, `PGDATA`, port **5432**, `gosu` step-down, and `/docker-entrypoint-initdb.d` first-boot scripts as the official `postgres` image, with the user-facing server and catalog bootstrap both provided by the pgrust binary at `/usr/local/bin/pgrust-postgres`. The only retained C tool is PostgreSQL 18 `psql` (init scripts and healthchecks). No C `postgres` backend and no C `initdb` are present in the final image.

## Image contract

| Surface | Value |
| --- | --- |
| Published image | `malisper/pgrust:v0.1` (pinned); `malisper/pgrust:latest` tracks the same release |
| Local tag (from clone) | `docker build -t pgrust .` |
| Binary (server + `--initdb`) | `/usr/local/bin/pgrust-postgres` |
| Client | `/usr/lib/postgresql/18/bin/psql` on `PATH` |
| Share / bootstrap templates | `/usr/share/postgresql/18` (`PG_SHAREDIR`) |
| Data volume | `/var/lib/postgresql/data` (`PGDATA`) |
| Socket dir | `/var/run/postgresql` |
| User | `postgres` uid/gid **999** |
| Port | **5432** (`EXPOSE`) |
| Stop signal | `SIGINT` (fast shutdown) |
| Entrypoint / default CMD | `docker-entrypoint.sh` / `["postgres"]` |

Official-compatible environment variables: `POSTGRES_PASSWORD`, `POSTGRES_USER`, `POSTGRES_DB`, `POSTGRES_INITDB_ARGS`, `POSTGRES_HOST_AUTH_METHOD`, `PGDATA`, plus Docker secrets via `*_FILE` (`file_env`).

## Quick run

<Tabs>
  <Tab title="Published image">
```bash
docker run -d --name pgrust \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  malisper/pgrust:v0.1

until docker exec -e PGPASSWORD=secret pgrust \
  psql -h 127.0.0.1 -U postgres -c '\q' >/dev/null 2>&1; do
  sleep 1
done

docker exec -it -e PGPASSWORD=secret pgrust \
  psql -h 127.0.0.1 -U postgres
```
  </Tab>
  <Tab title="Local image build">
```bash
docker build -t pgrust .
docker run --rm \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  pgrust
```
  </Tab>
</Tabs>

Connect from the host with any Postgres 18 client:

```bash
psql postgres://postgres:secret@localhost:5432/postgres \
  -c "select version(), 1 + 1 as two"
```

Or use the image-bundled `psql` via `docker exec` (as in the README one-liner).

<Warning>
pgrust is not production-ready. Prefer the pinned tag `malisper/pgrust:v0.1` for reproducible experiments.
</Warning>

## Image layout (three stages)

```text
Dockerfile stages
├── rustbuild  rust:1-bookworm
│   cargo build --release --locked --bin postgres
│   PGRUST_PGSHAREDIR → /usr/share/postgresql/18 (baked for tz)
│   → /opt/pgrust-postgres
├── pgtools    debian:bookworm-slim + PGDG
│   postgresql-18 (share tree only) + postgresql-client-18 (psql)
│   → /opt/pgbin/psql, share files, /opt/runlibs
└── final      debian:bookworm-slim
    pgrust-postgres + psql + share + gosu + runtime libs
    timezone → symlink to /usr/share/zoneinfo
```

| Stage | Role |
| --- | --- |
| `rustbuild` | Builds the pgrust `postgres` binary. Vendored `nodetags.h` means no PostgreSQL source tree is required—only Rust and build deps (`libicu-dev`, OpenSSL, LDAP, PAM). |
| `pgtools` | Installs PGDG `postgresql-18` / `postgresql-client-18`, stages **psql** and non-glibc shared libs, verifies share bootstrap files (`postgres.bki`, `system_*.sql`, samples, …). C server binaries and loadable modules are **not** copied forward. |
| `final` | Runtime: uid 999 `postgres`, `libicu`/`libxml2`/`tzdata`/`locales`/`gosu`, binary + share + `psql`, entrypoint, `VOLUME`, `EXPOSE 5432`. |

Build-time share path for the binary:

```dockerfile
ENV PGRUST_PGSHAREDIR=${PG_SHAREDIR}   # default /usr/share/postgresql/18
```

Debian packages use system tzdata (no `timezone/` under share). The final stage links:

```text
/usr/share/postgresql/18/timezone → /usr/share/zoneinfo
```

`.dockerignore` keeps the build context lean (drops `/target`, `/docs`, `/scripts`, `/audits`, …) while preserving crate sources the Cargo build needs.

## First-boot lifecycle

On `CMD`/`args` that run the server (`postgres` or leading `-` flags rewritten to `postgres …`), the entrypoint initializes only when `$PGDATA/PG_VERSION` is missing.

```mermaid
flowchart TD
  subgraph entry["docker-entrypoint.sh"]
    A["docker_setup_env"] --> B["docker_create_db_directories"]
    B --> C{"uid == 0?"}
    C -->|yes| D["gosu postgres re-exec"]
    C -->|no| E{"PG_VERSION exists?"}
    D --> E
    E -->|yes| Skip["Skip init message"]
    E -->|no| F["docker_verify_minimum_env"]
    F --> G["docker_init_database_dir\npgrust-postgres --initdb -L PG_SHAREDIR"]
    G --> H["pg_setup_hba_conf"]
    H --> I["docker_temp_server_start\nsocket-only pgrust"]
    I --> J["docker_setup_password ALTER ROLE"]
    J --> K["docker_setup_db CREATE DATABASE"]
    K --> L["docker_process_init_files\n/docker-entrypoint-initdb.d"]
    L --> M["docker_temp_server_stop SIGINT"]
    M --> N["Init complete message"]
  end
  Skip --> Run
  N --> Run["exec pgrust-postgres -D PGDATA + PGRUST_SERVER_OPTS + listen_addresses=*"]
```

Success signal on empty data dir (stdout):

```text
PostgreSQL init process complete; ready for start up.
```

Existing data dir:

```text
PostgreSQL Database directory appears to contain a database; Skipping initialization
```

### What diverges from docker-library/postgres

| Concern | Official image | pgrust image |
| --- | --- | --- |
| Catalog bootstrap | C `initdb` | `pgrust-postgres --initdb` (no `--pwfile`; password via `ALTER ROLE` after temp server start) |
| Temp server during init | `pg_ctl` | Background `pgrust-postgres` + poll with `psql` on unix socket |
| Final server argv | `postgres` on PATH | Rewrite to `/usr/local/bin/pgrust-postgres` + injected GUCs |
| Share location | Relative to bindir | Explicit `-L "$PG_SHAREDIR"` (binary lives under `/usr/local/bin`) |
| Host auth default | Password encryption discovery via `postgres -C` | Default **`trust`** for working TCP (password auth seam unported) |
| nss_wrapper | Used for username | Not needed; `--username` only |

## Injected runtime options

Applied to both the temporary init server and the final process (user CMD flags still append afterward):

| Setting | Value | Purpose |
| --- | --- | --- |
| `ulimit -s` | `65520` | Large C stack for pgrust frames |
| `RUST_MIN_STACK` | `33554432` (default if unset) | Rust thread stack |
| `io_method` | `sync` | Worker I/O method is unported |
| `max_stack_depth` | `60000` | Matches stack allowance |
| `unix_socket_directories` | `/var/run/postgresql` | Aligns with Debian `psql` default socket |
| `listen_addresses` (final only) | `*` | TCP on container networks |
| `listen_addresses` (temp init only) | empty | Socket-only, same as upstream |

Final exec shape:

```bash
exec "$PGRUST_BIN" -D "$PGDATA" \
  -c io_method=sync \
  -c max_stack_depth=60000 \
  -c unix_socket_directories=/var/run/postgresql \
  -c listen_addresses='*' \
  "$@"
```

Non-`postgres` commands (for example interactive `psql`, or help/version flags that should exit) are `exec`'d without rewrite.

## Environment variables

Defaults and first-boot behavior:

| Variable | Default | Notes |
| --- | --- | --- |
| `POSTGRES_PASSWORD` | (empty) | Required unless `POSTGRES_HOST_AUTH_METHOD=trust`. Stored in catalog via `ALTER ROLE` after init. Supports `POSTGRES_PASSWORD_FILE`. |
| `POSTGRES_USER` | `postgres` | Superuser for `--initdb --username`. Supports `*_FILE`. |
| `POSTGRES_DB` | same as `POSTGRES_USER` | Created after temp server starts if missing. Supports `*_FILE`. |
| `POSTGRES_INITDB_ARGS` | empty | Extra args to `--initdb`. **Space-separated** (`--opt value`), not `--opt=value`. Supports `*_FILE`. |
| `POSTGRES_INITDB_WALDIR` | unset | Creates dir and passes `--waldir` when set. |
| `POSTGRES_HOST_AUTH_METHOD` | **`trust`** (if unset at HBA setup) | Written as `host all all all <method>` in `pg_hba.conf`. |
| `PGDATA` | `/var/lib/postgresql/data` | Init skipped when `$PGDATA/PG_VERSION` is non-empty. |
| `PG_SHAREDIR` | `/usr/share/postgresql/18` | Passed as `-L` to `--initdb`. |
| `PGRUST_BIN` | `/usr/local/bin/pgrust-postgres` | Override path to the server binary. |
| `RUST_MIN_STACK` | `33554432` | Exported by the entrypoint. |
| `PGPORT` | `5432` | Temp server port during first-boot setup. |
| `PG_MAJOR` | `18` | Image major; used for old-data-layout checks. |

### Supported vs rejected initdb args

pgrust `--initdb` accepts a subset (for example `-D`/`--pgdata`, `-U`/`--username`, `-L`, `-E`/`--encoding`, locale flags, `--wal-segsize`). Unsupported flags in `POSTGRES_INITDB_ARGS` (for example `-A`/`--auth`, `--data-checksums`) **error** at first boot.

## Init scripts

Directory: `/docker-entrypoint-initdb.d` (created empty in the image). Processed only on first boot, after password and database setup, against a socket-only temp server.

| Pattern | Action |
| --- | --- |
| `*.sh` (executable) | Run |
| `*.sh` (not executable) | Source |
| `*.sql` | `psql -f` |
| `*.sql.gz` / `*.sql.xz` / `*.sql.zst` | Decompress pipe into `psql` |
| other | Ignored (logged) |

`psql` runs with `ON_ERROR_STOP=1`, `--username "$POSTGRES_USER"`, optional `--dbname "$POSTGRES_DB"`, and `PGHOST`/`PGHOSTADDR` cleared so the unix socket is used.

Example bind-mount:

```bash
docker run --rm \
  -e POSTGRES_PASSWORD=secret \
  -v "$PWD/initdb.d:/docker-entrypoint-initdb.d:ro" \
  -p 5432:5432 \
  pgrust
```

## Authentication (TCP)

<Warning>
Password authentication over TCP is not usable yet. The `fetch_role_password` seam is unported. A `host … scram-sha-256` or `md5` line causes `FATAL` **"seam not installed: fetch_role_password"** even with the correct password. Local/`trust` paths work (init scripts and unix-socket checks succeed).
</Warning>

Consequences for the Docker contract:

1. Default host method is **`trust`** so TCP from other containers or the host works without seam errors.
2. The superuser password is still written into the catalog when `POSTGRES_PASSWORD` is set; it is simply not enforced on `host` lines until password auth is ported.
3. Setting `POSTGRES_HOST_AUTH_METHOD` to a password method is honored in `pg_hba.conf` but the entrypoint prints a large warning that TCP logins will fail.
4. Empty `POSTGRES_PASSWORD` without `POSTGRES_HOST_AUTH_METHOD=trust` still errors at first boot (same guard as the official image).

For local experiments, prefer:

```bash
-e POSTGRES_PASSWORD=secret
# leave POSTGRES_HOST_AUTH_METHOD unset → trust host line
```

## Verify readiness

1. **Wait for first-boot completion** — look for `PostgreSQL init process complete; ready for start up.` in container logs, or poll with `psql` as in the quick-run loop.
2. **Query** — `select version(), 1 + 1 as two` should return a Postgres 18-shaped version string and `2`.
3. **Skip path** — restart the same named volume; logs should show *Skipping initialization* and the prior data should remain.

## Common failures

| Symptom | Cause | Mitigation |
| --- | --- | --- |
| Init errors: password not specified | Empty `POSTGRES_PASSWORD` and auth method not `trust` | Set `-e POSTGRES_PASSWORD=…` or `POSTGRES_HOST_AUTH_METHOD=trust` |
| TCP `FATAL` seam / `fetch_role_password` | Password host auth method | Use default `trust` for host lines |
| Initdb option error | Unsupported `POSTGRES_INITDB_ARGS` | Stick to supported flags; see [initdb options reference](/initdb-reference) |
| Temp server timeout | Share/tz path wrong or binary crash during bootstrap | Confirm image has `/usr/share/postgresql/18` and `timezone` symlink; check logs |
| Stack / boot refusal without Docker entrypoint | Missing stack/`io_method` when running the binary bare | Prefer the image entrypoint, or apply the same `ulimit`/`RUST_MIN_STACK`/`io_method=sync` settings as [Run the server](/run-server) |
| Old data layout error (18+ mount layout) | Existing volume with unexpected `PG_VERSION` paths | Match official 18+ volume guidance; see entrypoint `docker_error_old_databases` |

## Related pages

<CardGroup>
  <Card title="Docker environment reference" href="/docker-reference">
    Full env surface: POSTGRES_*, PGDATA, init scripts, injected server opts.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    Non-Docker local build, --initdb, and connect path.
  </Card>
  <Card title="Run the server" href="/run-server">
    Postmaster flags, io_method=sync, stack settings, readiness.
  </Card>
  <Card title="Initialize a cluster" href="/init-cluster">
    postgres --initdb phases and success signals outside Docker.
  </Card>
  <Card title="Compatibility and status" href="/compatibility">
    Non-production status, extension boundaries, regression-oracle model.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    io_method, stack, share/tz, auth, and client failures.
  </Card>
</CardGroup>

---

## 05. Compatibility and status

> Postgres 18.3 behavior and on-disk compatibility, regression-oracle model, non-production status, and extension/PL boundaries that block generic Postgres extensions.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/05-compatibility-and-status.md
- Generated: 2026-07-10T05:29:45.993Z

### Source Files

- `README.md`
- `crates/common/controldata_utils/src/lib.rs`
- `crates/backend/postmaster/postmaster/src/main_entry.rs`
- `crates/pl/plpgsql/src/handler/src/lib.rs`
- `crates/backend/utils/fmgr/dfmgr_seams/src/lib.rs`
- `scripts/run-regression`

---
title: "Compatibility and status"
description: "Postgres 18.3 behavior and on-disk compatibility, regression-oracle model, non-production status, and extension/PL boundaries that block generic Postgres extensions."
---

pgrust ships a single `postgres` server binary that targets **PostgreSQL 18.3** wire and SQL behavior, reuses the **same on-disk control and catalog version** as stock 18.3, and treats the **vendored 18.3 regression expected files** as the behavioral oracle. It is **not production-ready**, is **not performance-optimized**, and does **not** load arbitrary third-party or unported C extensions the way stock Postgres does.

## Status snapshot

| Area | Current contract |
|------|------------------|
| Target version | PostgreSQL **18.3** (`server_version` / `server_version_num` = `18.3` / `180003`) |
| Behavioral oracle | Vendored `vendor/postgres-18.3/regress/{sql,expected}` via `scripts/run-regression` |
| Launch claim | Matched expected output across **46,000+** regression queries |
| Production | **Not production-ready**; not performance-optimized |
| On-disk | Boots a **Postgres 18.3** `PGDATA` when control/catalog layout matches |
| Generic extensions | **Not generally compatible** — only in-process **builtin** libraries |
| Other PLs | **PL/Python, PL/Perl, PL/Tcl** not generally available |
| Ported PL | **PL/pgSQL** registered as builtin library `plpgsql` |
| Runtime I/O | Supported path requires **`io_method=sync`** (default GUC is `worker`) |

<Warning>
Use pgrust for exploration, demos, regression work, and architecture experiments — not as a drop-in production replacement for PostgreSQL.
</Warning>

## Version identity

Preset GUCs report stock 18.3 numbers; `version()` is deliberately branded.

| Surface | Value |
|---------|--------|
| `server_version` | `18.3` |
| `server_version_num` / `PG_VERSION_NUM` | `180003` |
| `PG_VERSION` / `PG_MAJORVERSION` (data dir) | `18.3` string / major **`18`** |
| `PG_CONTROL_VERSION` | `1800` |
| `CATALOG_VERSION_NO` | `202506291` |
| SQL `version()` | Banner **`pgrust 18.3 on …`** (not `"PostgreSQL 18.3 …"`) |

`server_version` and `server_version_num` stay Postgres-shaped for clients and tooling. The `version()` text is a known cosmetic divergence; regression tests only regex platform tokens such as `linux-gnu`, not the product name.

```sql
SELECT version();
-- e.g. pgrust 18.3 on aarch64-darwin, compiled by clang-21.0.0, 64-bit

SHOW server_version;
-- 18.3
```

## On-disk compatibility

pgrust can create a cluster with `postgres --initdb` against `vendor/postgres-18.3/share`, or start against an **existing Postgres 18.3** data directory when the same major version and control-file layout apply.

### What is checked

```text
PGDATA/
├── PG_VERSION              → major must equal this binary's major (18)
└── global/pg_control       → fixed LP64 ControlFileData image + CRC
                              pg_control_version == 1800
                              catalog_version_no == 202506291
                              BLCKSZ, MAXALIGN, float format, segment sizes, …
```

| Check | Owner behavior | Failure signal |
|-------|----------------|----------------|
| `PG_VERSION` major | `ValidatePgVersion` | `database files are incompatible with server` / invalid data directory |
| `pg_control` present | postmaster `checkControlFile` | cannot find system; exit path |
| `pg_control_version` | `get_controlfile` | wrong version or **byte-ordering mismatch** |
| CRC | returned as `crc_ok` (not always fatal at read) | catalog SQL / recovery paths report checksum mismatch |
| Catalog / compile constants | control-file compatibility checks at startup | `database files are incompatible with server: CATALOG_VERSION_NO …` (and peers) |

Native-endian decode of the control image means a cluster written on a different byte order fails with an explicit **byte ordering mismatch** rather than silent corruption.

### Practical boundaries

- Same **major** (18) and **catalog version** (`202506291`) as stock 18.3 are required.
- A clean match of layout/constants is the compatibility bar — not a guarantee for every recovery, extension, or WAL edge case.
- Wrong share / timezone trees break init and boot even when `PGDATA` is valid; bake or pass `-L` to `vendor/postgres-18.3/share`.

Operational detail for reusing an existing cluster: [Boot from existing Postgres data](/existing-data-dir).

## Behavioral oracle (regression model)

Compatibility is measured against **unmodified** PostgreSQL 18.3 SQL and expected outputs, not against a hand-written pgrust suite.

| Input | Role |
|-------|------|
| `vendor/postgres-18.3/regress/sql/*.sql` | Test scripts |
| `vendor/postgres-18.3/regress/expected/*.out` (+ `_1` / `_2` variants) | Oracle outputs |
| `vendor/postgres-18.3/regress/parallel_schedule` | Default full-suite order |
| `target/release/postgres` | Server under test (`--initdb` + postmaster) |
| PostgreSQL 18 `psql` | Client only (external dependency) |

`scripts/run-regression`:

1. Runs pgrust `--initdb` with `-L vendor/postgres-18.3/share`.
2. Starts postmaster with **`io_method=sync`**, raised stack settings, and a short Unix socket path.
3. Creates database `regression` and runs each SQL file through `psql`.
4. Diffs stdout against the best matching expected file (`PASS` only when diff length is 0).
5. Treats server death as `CRASHED` and non-zero `psql` as `PSQL_FAIL`.

```bash
PGRUST_BIN="$PWD/target/release/postgres" scripts/run-regression
# optional: scripts/run-regression select test names…
```

Vendored data is **verbatim** from PostgreSQL 18.3 (`REL_18_3`): installed `share/`, regress SQL/expected/schedule, and isolation assets for separate harnesses. Isolation/auth/recovery TAP runners exist alongside the clone-only main suite; they are not the primary 46k-query claim surface.

## Extension and PL boundaries

### Loader model

Stock Postgres resolves `LANGUAGE C` / `MODULE_PATHNAME` via `dlopen` of `$libdir/<module>.so`. pgrust’s Rust backend **does not expose a C ABI** for arbitrary shared libraries. Ported modules register into an in-process **builtin library** table (`dfmgr_seams::register_builtin_library`). `load_file` / `load_external_function` consult that table first; only unregistered names fall through to the OS loader path.

```text
CREATE EXTENSION / LOAD / prosrc
        │
        ▼
  strip path + .so → basename (e.g. citext, plpgsql, regress)
        │
        ├─ builtin registry hit → in-process Rust functions + optional pg_init
        │
        └─ miss → OS dlopen/dlsym path (generic C extensions fail here)
```

Unported modules therefore fail at load or symbol resolution even when SQL scripts exist on disk. Output plugins (for example `test_decoding`) use a parallel **builtin output-plugin** registry for the same reason.

### Ported contrib / test builtins

Registered **simple library names** (suffix-free):

| Builtin name | Notes |
|--------------|--------|
| `citext` | Vendored `.control` / SQL under `share/extension/` |
| `hstore` | Same |
| `ltree` | Same |
| `pg_trgm` | Same |
| `pgcrypto` | Same; custom GUC `pgcrypto.builtin_crypto_enabled` |
| `pg_stat_statements` | Same |
| `uuid-ossp` | Same |
| `injection_points` | Same |
| `plpgsql` | Language handler; vendored `plpgsql.control` |
| `pg_prewarm` | Builtin library registered; not all autoprewarm surfaces may be complete |
| `regress` | In-process regress helpers for the SQL suite (path need not exist on disk) |
| `test_decoding` | Builtin **output plugin**, not a generic `.so` |

Workspace also contains partial contrib work (for example `earthdistance` without full fmgr/`CREATE EXTENSION` wiring, `amcheck` crates without the same builtin registration pattern). Treat those as **incomplete ports**, not drop-in extensions.

### What is blocked

| Workload | Status |
|----------|--------|
| Arbitrary `CREATE EXTENSION` of community C modules | **Blocked** without a pgrust builtin port |
| PL/Python, PL/Perl, PL/Tcl | **Not generally compatible** |
| PL/pgSQL | **Ported as builtin** `plpgsql` (with remaining loud seams for some SPI/exec/GUC init edges) |
| Preload of unported `.so` via `shared_preload_libraries` | **Fails** unless the name is a registered builtin |
| Logical decoding plugins other than registered builtins | **Builtin registry only** for ported plugins |

Vendored `share/extension/*.control` today covers the ported set above (plus `plpgsql`), not the full upstream contrib catalog. `CREATE EXTENSION` still needs matching control/SQL in the share tree **and** a registered builtin body.

## Required runtime posture (compatibility-affecting)

These settings are not optional for a working multiuser boot path in the documented runners and Docker image:

| Setting | Required / typical | Why it matters |
|---------|-------------------|----------------|
| `io_method` | **`sync`** | Only I/O method the port supports end-to-end; GUC default remains `worker` |
| `max_stack_depth` | e.g. `60000` | Deep recursion / plan paths |
| `RUST_MIN_STACK` | e.g. `33554432` | Rust thread stack floor |
| `ulimit -s` | e.g. `65520` | Process stack headroom |
| Share dir | `PGRUST_PGSHAREDIR` at build and/or `-L` at init | Bootstrap catalogs, TZ, extensions |

Without `io_method=sync`, AIO worker paths are not a supported compatibility surface.

## Compatibility matrix (summary)

| Capability | Compatible? |
|------------|-------------|
| SQL / catalog behavior vs PG 18.3 regress oracle | Yes, as measured by `scripts/run-regression` |
| Protocol clients / `psql` 18 | Yes (client is external) |
| On-disk 18.3 `PGDATA` (same arch/endian, matching control constants) | Intended yes |
| Cross-endian or non-18 major data | No |
| Generic C extensions / other PLs | No (builtins only) |
| Production HA / performance SLAs | No claim |
| Multithreaded postmaster, pooling, experimental storage | Roadmap only |

## Failure modes

| Symptom | Likely cause |
|---------|----------------|
| `byte ordering mismatch in control file` | Endian / wrong machine’s `pg_control` |
| `PG_CONTROL_VERSION … but the server was compiled with …` | Control version or byte-swap misread |
| `database files are incompatible with server` + `CATALOG_VERSION_NO` | Not a 18.3 catalog, or wrong binary |
| `could not load library` / missing function in file | Unported extension; not in builtin registry |
| Extension SQL not found | Control/SQL not under vendored `share/extension/` |
| Boot hangs / AIO errors | Missing `-c io_method=sync` |
| Stack overflow | Missing `RUST_MIN_STACK` / `max_stack_depth` / `ulimit -s` |
| Root refused | Same root check as Postgres for normal modes |

## Next

<CardGroup cols={2}>
  <Card title="Boot from existing Postgres data" href="/existing-data-dir">
    Control-file and major-version constraints when reusing a stock 18.3 PGDATA.
  </Card>
  <Card title="Use contrib extensions" href="/contrib-extensions">
    Builtin modules, CREATE EXTENSION against vendored control/SQL, and unported gaps.
  </Card>
  <Card title="Run regression tests" href="/regression-tests">
    Clone-only scripts/run-regression, psql client, selective tests, TAP harnesses.
  </Card>
  <Card title="Seams and builtin libraries" href="/seams-and-builtins">
    How registration replaces dlopen for ported contrib and regress helpers.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    io_method, stack, share/tz paths, initdb gaps, and unported extension loads.
  </Card>
  <Card title="Overview" href="/overview">
    Who should use pgrust, status limits, and first docs routes.
  </Card>
</CardGroup>

---

## 06. Process modes and dispatch

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

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/06-process-modes-and-dispatch.md
- Generated: 2026-07-10T05:29:20.080Z

### 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>

---

## 07. Install paths and share directory

> PGRUST_PGSHAREDIR and related build-time path env vars, runtime -L override, vendored share tree contents, and why wrong share/tz paths break boot.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/07-install-paths-and-share-directory.md
- Generated: 2026-07-10T05:29:51.807Z

### Source Files

- `.cargo/config.toml`
- `crates/backend/utils/init/miscinit/src/boot_paths.rs`
- `crates/backend/main/initdb/src/lib.rs`
- `Dockerfile`
- `vendor/postgres-18.3/share/postgres.bki`
- `vendor/postgres-18.3/share/postgresql.conf.sample`

---
title: "Install paths and share directory"
description: "PGRUST_PGSHAREDIR and related build-time path env vars, runtime -L override, vendored share tree contents, and why wrong share/tz paths break boot."
---

pgrust bakes PostgreSQL-style install directories into the `postgres` binary at compile time via `option_env!("PGRUST_*")` in `boot_paths`, then derives a runtime share path with `get_share_path(my_exec_path)`. Initdb can override that path for bootstrap templates with `-L`; timezone data, abbreviation sets, extension control files, and tsearch dictionaries still resolve through the baked-in share path at server runtime.

## Path model

```text
Build-time env (option_env!)
  PGRUST_PGSHAREDIR, PGRUST_PGBINDIR, PGRUST_PKGLIBDIR, ...
        │
        ▼
  Binary literals (defaults: /usr/local/pgsql/{share,bin,lib,...})
        │
        ▼
  get_share_path(my_exec_path)
    make_relative_path(PGSHAREDIR, PGBINDIR, exec)
        │
        ├─ initdb: sharedir (or -L override)
        │     postgres.bki, *.conf.sample, system_*.sql, ...
        │
        └─ server runtime (always derived, not -L)
              share/timezone       → pg_TZDIR / pg_open_tzfile
              share/timezonesets   → read_tz_file
              share/extension      → CREATE EXTENSION control/SQL
              share/tsearch_data   → text search dictionaries
```

| Concern | Who sets path | Mechanism |
|---------|---------------|-----------|
| Share / bindir / pkglib / etc. literals | Build | `PGRUST_*` env → `option_env!` |
| Resolved share path | Boot / initdb | `get_share_path` + `my_exec_path` |
| Initdb templates only | CLI | `-L <sharedir>` |
| Docker image share | Image build + entrypoint | `PGRUST_PGSHAREDIR` bake + `-L "$PG_SHAREDIR"` |

<Warning>
`PGRUST_PGSHAREDIR` is **compile-time only**. Exporting it when *running* the binary does not relocate share/tz paths. Rebuild with the desired value (or pass `-L` for initdb template loading only).
</Warning>

## Build-time path environment variables

`crates/backend/utils/init/miscinit/src/boot_paths.rs` mirrors PostgreSQL’s configure-time install dirs (`pg_config_paths.h`). Each value is read with `option_env!("PGRUST_…")` and falls back to the documented `/usr/local/pgsql/...` layout.

| Env var | Default | Used by |
|---------|---------|---------|
| `PGRUST_PGSHAREDIR` | `/usr/local/pgsql/share` | `get_share_path` → tz, timezonesets, extensions, tsearch, initdb default |
| `PGRUST_PGBINDIR` | `/usr/local/pgsql/bin` | Relativization base for all `make_relative_path` targets |
| `PGRUST_PKGLIBDIR` | `/usr/local/pgsql/lib` | `get_pkglib_path` / `pkglib_path` (standalone boot) |
| `PGRUST_SYSCONFDIR` | `/usr/local/pgsql/etc` | `get_etc_path` / `SYSCONFDIR` config row |
| `PGRUST_LIBDIR` | `/usr/local/pgsql/lib` | `get_lib_path` |
| `PGRUST_INCLUDEDIR` | `/usr/local/pgsql/include` | `get_include_path` |
| `PGRUST_PKGINCLUDEDIR` | `/usr/local/pgsql/include` | `get_pkginclude_path` |
| `PGRUST_INCLUDEDIRSERVER` | `/usr/local/pgsql/include/server` | `get_includeserver_path` |
| `PGRUST_LOCALEDIR` | `/usr/local/pgsql/share/locale` | `get_locale_path` |
| `PGRUST_DOCDIR` | `/usr/local/pgsql/share/doc` | `get_doc_path` |
| `PGRUST_HTMLDIR` | `/usr/local/pgsql/share/doc` | `get_html_path` |
| `PGRUST_MANDIR` | `/usr/local/pgsql/share/man` | `get_man_path` |

`PGRUST_PGSHAREDIR` is the high-impact setting: wrong bake-in points timezone and abbreviation loading at a nonexistent or incomplete tree.

### Repository defaults

`.cargo/config.toml` sets:

```toml
[env]
PGRUST_PGSHAREDIR = "/tmp/pgrust_share"
```

Cargo applies this only when the variable is **unset** in the process environment (`force` is not enabled). Explicit:

```bash
PGRUST_PGSHAREDIR=/somewhere cargo build
```

still wins.

README’s source-build recipe bakes the vendored tree instead:

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
cargo build --release --locked --bin postgres
```

| Build context | Typical `PGRUST_PGSHAREDIR` |
|---------------|----------------------------|
| Plain `cargo build` (repo config) | `/tmp/pgrust_share` |
| Documented local release build | `$PWD/vendor/postgres-18.3/share` |
| Docker `rustbuild` stage | `/usr/share/postgresql/18` (`ARG PG_SHAREDIR`) |

Ensure the path you bake actually exists and contains `timezone/`, `timezonesets/`, and (for initdb) the bootstrap files listed below—or pass a correct `-L` at initdb time and keep the bake-in correct for server runtime.

### Relative vs absolute resolution

`get_share_path(my_exec_path)` calls `make_relative_path(PGSHAREDIR, PGBINDIR, my_exec_path)`:

1. If the running binary’s directory layout still matches the common prefix of the configured bindir/sharedir, the share path is **relocated** relative to the executable.
2. Otherwise the result is the **configured absolute** `PGSHAREDIR` (after canonicalize).

Docker installs the binary at `/usr/local/bin/pgrust-postgres`, outside a PGDG-style bindir, so relative derivation does **not** find the share tree—entrypoint always passes `-L` for initdb, and the image bakes `PGRUST_PGSHAREDIR` to the absolute PGDG share path for runtime tz.

Wasm builds: `find_my_exec` returns the sentinel `/postgres` (no real executable on disk); share is expected to come from the bake-in / host VFS.

## Runtime `-L` (initdb only)

`postgres --initdb` / `postgres initdb` parses `-L` as a share-directory override:

<ParamField body="-L" type="path" required={false}>
Share directory used for bootstrap templates and post-bootstrap SQL. When omitted, initdb uses `get_share_path(current_exe)`.
</ParamField>

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

| Phase | Reads from `sharedir` |
|-------|------------------------|
| Config scaffolding | `postgresql.conf.sample`, `pg_hba.conf.sample`, `pg_ident.conf.sample` |
| Bootstrap (`--boot`) | `postgres.bki` (header must be `# PostgreSQL 18`) |
| Post-bootstrap (`--single`) | `system_constraints.sql`, `system_functions.sql`, `system_views.sql`, `snowball_create.sql`, `information_schema.sql`, `sql_features.txt` (via `COPY`) |

Missing or wrong `-L` surfaces as read failures such as `could not read "<sharedir>/postgres.bki": ...` or a BKI header mismatch for a non-18 tree.

<Note>
`-L` does **not** redirect the postmaster’s timezone or extension share lookups. Those always use `get_share_path` from the compiled-in dirs + `my_exec_path`.
</Note>

Docker entrypoint (always explicit `-L`):

```bash
"$PGRUST_BIN" --initdb -D "$PGDATA" -L "$PG_SHAREDIR" --username "$POSTGRES_USER" ...
```

with `PG_SHAREDIR="${PG_SHAREDIR:-${PGRUST_PGSHAREDIR:-/usr/share/postgresql/18}}"`.

## Vendored share tree

Clone-local share data lives at `vendor/postgres-18.3/share` (built/installed `share/postgresql/` from PostgreSQL 18.3, not raw source). Documented purpose: bootstrap + runtime data without an external PG install tree.

:::files
vendor/postgres-18.3/share/
  postgres.bki                 # catalog bootstrap input (~1MB built BKI)
  system_constraints.sql
  system_functions.sql
  system_views.sql
  information_schema.sql
  sql_features.txt
  snowball_create.sql
  postgresql.conf.sample
  pg_hba.conf.sample
  pg_ident.conf.sample
  pg_service.conf.sample
  psqlrc.sample
  errcodes.txt
  timezone/                    # IANA tzdb tree (America/, Europe/, ...)
  timezonesets/                # Default, Australia, India, *.txt region sets
  tsearch_data/                # text search dictionaries / stopwords
  extension/                   # *.control + versioned extension SQL
:::

| Subtree | Consumer |
|---------|----------|
| Root `*.bki` / `system_*.sql` / samples | `--initdb` bootstrap and config setup |
| `timezone/` | `pg_tzdir()` → `<share>/timezone`; `pg_open_tzfile` |
| `timezonesets/` | `read_tz_file` → `<share>/timezonesets/<name>` |
| `extension/` | `get_extension_control_directories` → `<share>/extension` |
| `tsearch_data/` | tsearch loaders via `get_share_path` |

`postgres.bki` first line is `# PostgreSQL 18`. Initdb rejects a share tree whose header does not match major version `18`.

### Docker share layout differences

The image does not ship the git-vendored tree. Stage `pgtools` installs PGDG `postgresql-18` and copies `/usr/share/postgresql/18` into the final image. Debian PG is built `--with-system-tzdata`, so the package share has **no** `timezone/` directory; the Dockerfile links it:

```dockerfile
ln -snf /usr/share/zoneinfo "${PG_SHAREDIR}/timezone"
```

The rustbuild stage sets `ENV PGRUST_PGSHAREDIR=${PG_SHAREDIR}` so the binary’s runtime tz path matches that location.

## Consumers of the share path

| Component | Path formula | Failure if wrong |
|-----------|--------------|------------------|
| Initdb (default) | `get_share_path(exe)` or `-L` | No BKI / samples / system SQL |
| Timezone names | `get_share_path` + `/timezone` | Zone load fails; session/log timezone broken |
| Abbreviation sets | `get_share_path` + `/timezonesets/<file>` | `DirectoryMissing` / file open errors on incomplete install |
| Extensions | `get_share_path` + `/extension` | Missing `.control` / script SQL |
| Full-text search | `get_share_path` + `/tsearch_data/...` | Dictionary / config file not found |

`common_path_seams::get_share_path` is installed from miscinit (`boot_paths::get_share_path`). Timezone code memoizes `pg_TZDIR` once per backend.

## Why wrong share/tz paths break boot

1. **Initdb cannot scaffold catalogs** without a readable `postgres.bki` and post-bootstrap SQL under the effective share dir.
2. **Server boot and SQL that touch timestamps/timezones** open files under `<share>/timezone`. A baked path pointing at `/usr/local/pgsql/share` (default when `PGRUST_PGSHAREDIR` was never set) or an empty `/tmp/pgrust_share` yields open failures.
3. **Abbreviation loading** probes `<share>/timezonesets`; if the directory is missing, `TzFileOpenError::DirectoryMissing` carries the directory path and `my_exec_path` for the incomplete-install diagnostic path.
4. **Relocated binaries** without matching bindir/sharedir layout fall back to the absolute baked share path—if that absolute path is wrong on the host, nothing relocates it.
5. **Docker without `-L`** would initdb against a derived path that cannot find PGDG share; the entrypoint therefore always passes `-L`.

<Check>
After a successful bake and initdb, verify:

- `test -f "$SHARE/postgres.bki" && head -1 "$SHARE/postgres.bki"` → `# PostgreSQL 18`
- `test -d "$SHARE/timezone" && test -d "$SHARE/timezonesets"`
- Server starts with required stack/`io_method` settings and `SELECT now();` succeeds
</Check>

## Practical recipes

<Tabs>
<Tab title="Local clone (README)">
```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
  cargo build --release --locked --bin postgres

target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale --encoding UTF8 -U postgres
```
Bake and `-L` both point at the vendored tree so initdb templates and runtime tz agree.
</Tab>
<Tab title="Default cargo config">
```bash
# .cargo/config.toml → /tmp/pgrust_share
# Populate or symlink first:
mkdir -p /tmp/pgrust_share
# e.g. rsync or ln -s the vendored share contents

cargo build --release --locked --bin postgres
```
Must keep `/tmp/pgrust_share` populated; the default is only a path string, not an auto-populated tree.
</Tab>
<Tab title="Docker">
```bash
docker build -t pgrust .
# Image bakes PGRUST_PGSHAREDIR=/usr/share/postgresql/18
# entrypoint always: --initdb -L "$PG_SHAREDIR"
docker run --rm -e POSTGRES_PASSWORD=secret -p 5432:5432 pgrust
```
</Tab>
</Tabs>

## Failure modes (share/path)

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `could not read ".../postgres.bki"` | Missing/wrong `-L` or empty derived share | Pass `-L` to vendored or image share |
| BKI header mismatch | Share from wrong PG major | Use PostgreSQL 18 share (`# PostgreSQL 18`) |
| Timezone open failures / incomplete install hint | Baked `PGRUST_PGSHAREDIR` wrong or tree lacks `timezone/` / `timezonesets/` | Rebuild with correct path; ensure tree complete (Docker: zoneinfo symlink) |
| Extension control not found | Share lacks `extension/` or path derives elsewhere | Point bake-in at tree that includes `extension/`; see contrib docs |
| Works after rebuild only | Changed env at runtime only | Rebuild—paths are compile-time |

## Related pages

<CardGroup cols={2}>
  <Card title="Build from source" href="/build-from-source">
    Host packages, bake-in, `cargo build --release --locked --bin postgres`.
  </Card>
  <Card title="Initialize a cluster" href="/init-cluster">
    `--initdb` phases, directory layout, success text.
  </Card>
  <Card title="initdb options reference" href="/initdb-reference">
    Full `-L` / `-D` / locale flag set and unsupported options.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Runtime GUCs plus build-time `PGRUST_*` path vars.
  </Card>
  <Card title="Docker" href="/docker">
    Image contract and first-boot entrypoint behavior.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Wrong share/tz path among known failure modes.
  </Card>
</CardGroup>

---

## 08. Seams and builtin libraries

> Seam registration model, seams-init aggregation, and dfmgr builtin-library registration that replaces dlopen for ported contrib and regress helpers.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/08-seams-and-builtin-libraries.md
- Generated: 2026-07-10T05:29:41.269Z

### Source Files

- `crates/_support/seam/seam_core/src/lib.rs`
- `crates/_support/seam/init/src/lib.rs`
- `crates/_support/seam/init/src/bin/postgres.rs`
- `crates/backend/utils/fmgr/dfmgr_seams/src/lib.rs`
- `crates/backend/test_regress/src/lib.rs`
- `crates/contrib/pg_stat_statements/src/lib.rs`

---
title: "Seams and builtin libraries"
description: "Seam registration model, seams-init aggregation, and dfmgr builtin-library registration that replaces dlopen for ported contrib and regress helpers."
---

pgrust links the server as one Rust `postgres` binary. Cross-crate calls that would form a Cargo dependency cycle go through **seams** (`OnceLock` function slots). Ported shared libraries (contrib modules, `regress`, PL/pgSQL, snowball, logical-decoding plugins) never `dlopen` a `.so`: they register into an in-process **builtin library** table that `dfmgr` consults before the OS loader.

## Architecture

```mermaid
flowchart TB
  subgraph bin["postgres binary"]
    main["init/src/bin/postgres.rs main()"]
    top["TopMemoryContext leak"]
    initall["init::init_all()"]
    pgmain["main_main::pg_main"]
    main --> top --> initall --> pgmain
  end

  subgraph seams["Seam layer"]
    core["seam_core::seam!"]
    xseams["X_seams crates<br/>declare slots"]
    owners["Owner crates<br/>init_seams() → slot::set"]
    core --> xseams
    owners --> xseams
    initall --> owners
  end

  subgraph builtins["Builtin library layer"]
    reg["dfmgr_seams BUILTIN_LIBRARIES"]
    regplugin["dfmgr_seams BUILTIN_OUTPUT_PLUGINS"]
    dfmgr["fmgr_dfmgr installers"]
    owners -->|"register_builtin_library"| reg
    owners -->|"register_builtin_output_plugin"| regplugin
    dfmgr -->|"builtin_library_present / resolve_*"| reg
    dfmgr -->|"resolve / invoke builtin plugin"| regplugin
  end

  subgraph sql["SQL / catalog path"]
    create["CREATE FUNCTION / CREATE EXTENSION<br/>LANGUAGE C AS '$libdir/…'"]
    load["LOAD 'module'"]
    create --> dfmgr
    load --> dfmgr
  end
```

| Layer | Crate / path | Role |
| --- | --- | --- |
| Macro | `seam_core` (`crates/_support/seam/seam_core`) | `seam!` → module with `set` / `call` / `is_installed` |
| Aggregator | `init` (`crates/_support/seam/init`) | `init_all()` calls every ported crate’s `init_seams()` |
| Binary | `postgres` bin in `init` | Leaks top memory context, runs `init_all()`, then `pg_main` |
| Declarations | `*_seams` crates (e.g. `heapam_seams`, `dfmgr_seams`) | Own the slot types; no implementations |
| Installers | Ported owner crates | `init_seams()` installs owned slots and/or registers builtins |
| Dynamic load | `fmgr_dfmgr` + `dfmgr_seams` | Registry-aware `load_file` / `load_external_function` / output plugins |

## Seam model

A seam exists only where a direct Cargo dependency would create a cycle. Declarations for crate X’s functions live in an `X_seams` crate; crate X installs them from `init_seams()`; `init::init_all()` invokes every such installer once at process start.

### `seam!` expansion

`seam_core::seam!(pub fn name(...) -> ...)` expands to a module `name` with:

| API | Behavior |
| --- | --- |
| `set(implementation)` | Installs into a process-global `OnceLock`. Second install panics: `seam installed twice: <module_path>` |
| `call(...)` | Invokes the installed body. Missing install panics: `seam not installed: <module_path>` |
| `is_installed()` | `true` after a successful `set` |
| `Signature` | `fn(...)` or higher-ranked `for<'a, ...> fn(...)` when lifetimes are declared |

There is no silent fallback. Optional feature `trace-seams` records hit/miss via `trace::Category::Seam`.

Lifetime:

```rust
// In an X_seams crate — declaration only
seam_core::seam!(
    pub fn vacuum_rel(relid: types_core::Oid) -> types_error::PgResult<()>
);

// In the owner crate's init_seams()
vacuum_rel_seams::vacuum_rel::set(my_vacuum_rel_impl);
```

Lifetime-generic form (allocator lifetimes, e.g. `Mcx<'mcx>`) is supported: the stored pointer is `for<'a> fn(...)`.

### Ownership rules

1. **Declare** in `*_seams` (depends on `seam_core` + shared types only).
2. **Install** from the owning unit’s `pub fn init_seams()` via `::set`.
3. **Call** from dependents via `::call` — dependents depend on `*_seams`, not on the owner (that is the cycle break).
4. Wire the owner into `init::init_all()` (and add a path dep in `crates/_support/seam/init/Cargo.toml`).

Uninstalled seams panic on first use. Double-install panics at boot when two crates both `set` the same slot.

## Startup aggregation (`init`)

The Cargo package is `init` at `crates/_support/seam/init` (repo comments often say “seams-init”). It holds **no** business logic and **no** `set()` calls of its own: one `crate::init_seams()` line per ported crate, kept sorted.

```rust
// crates/_support/seam/init/src/lib.rs (shape)
pub fn init_all() {
    // One line per ported crate…
    pg_stat_statements::init_seams();
    test_regress::init_seams();
    // …hundreds more backend / contrib / catalog units…
}
```

### Process entry

`crates/_support/seam/init/src/bin/postgres.rs` mirrors `src/backend/main/main.c`:

1. Build `argv`.
2. Leak `TopMemoryContext` and obtain a `'static` `mcx`.
3. **`init::init_all()`** — seam registry + builtin registrations must be complete before any cross-crate call.
4. `pg_main(mcx, &argv)` → postmaster / single-user / bootstrap / `--initdb` / print-and-exit paths.

```text
postgres main
  → MemoryContext "TopMemoryContext"
  → init::init_all()          # seams + builtin libraries
  → pg_main(mcx, argv)
```

### Guards and baseline

| Check | What it enforces |
| --- | --- |
| Integration tests under `init` | Every crate that defines `init_seams` is reachable from `init_all` (static wiring) |
| `tests/no_double_install.rs` | Full `init_all()` in a clean process completes without `seam installed twice` |
| `builtin_gap_baseline` | Live `missing_builtins()` after `init_all()` matches the checked-in OID/name gap set |

When porting a new unit: implement `init_seams()`, add dependency + call line in `init`, run the aggregator tests.

## Builtin libraries (dlopen replacement)

The Rust backend does not expose a C ABI suitable for loading stock `.so` modules. SQL that does `LANGUAGE C AS '$libdir/<name>', '<symbol>'` still works for **ported** modules because `dfmgr` resolves them in-process.

### Registry API (`dfmgr_seams`)

| Item | Purpose |
| --- | --- |
| `BuiltinLibraryEntry { name, lookup, pg_init }` | One ported shared library |
| `register_builtin_library(entry)` | Called from the module crate’s `init_seams()`; replace-on-same-name (idempotent re-init) |
| `registry_library_present(library)` | Name in table? |
| `registry_resolve(library, function)` | Symbol → `Option<LoadedExternalFunc>` |
| `registry_pg_init(library)` | Optional `_PG_init`-equivalent |

Seam consult surface (installed once by `fmgr_dfmgr::init_seams`):

- `builtin_library_present(library) -> bool`
- `resolve_builtin_library_function(library, function) -> PgResult<Option<LoadedExternalFunc>>`

Also declared on the same crate: `load_file`, `load_external_function`, and logical-decoding load/dispatch seams.

### Name keying

`fmgr_dfmgr::simple_library_name` reduces a `probin` / load path to a registry key:

- `$libdir/regress` → `regress`
- `/path/to/pg_stat_statements.so` → `pg_stat_statements`
- Strips known suffixes `.so` / `.dylib` / `.dll` (key is not opened as a file)

### Load and resolve path

**`load_external_function(probin, prosrc, …)`** (CREATE FUNCTION / fmgr resolve):

1. `simple_library_name(probin)` → key.
2. If `builtin_library_present(key)`:
   - Run `pg_init` once per backend (`run_builtin_pg_init_once`).
   - `resolve_builtin_library_function(key, prosrc)`.
   - Missing symbol → error: `could not find function "<prosrc>" in file "<probin>"` (`ERRCODE_UNDEFINED_FUNCTION`).
3. Else fall through to the OS dynamic loader path (fails in this build for unported C modules).

**`load_file` / `LOAD`** (utility):

1. Same present check.
2. On hit: `run_builtin_pg_init_once` then success (no OS open).
3. Else OS path.

`pg_init` must be **idempotent** at the registry level; the loader also de-duplicates with a per-backend “already inited” set so hooks/GUCs run once, matching C’s first-load `_PG_init`.

### Registered builtin libraries

| Registry `name` | Owner area | Typical `pg_init` |
| --- | --- | --- |
| `regress` | `backend/test_regress` | `None` |
| `citext` | `contrib/citext` | `None` |
| `hstore` | `contrib/hstore` | `None` |
| `ltree` | `contrib/ltree` | `None` |
| `pg_trgm` | `contrib/pg_trgm` | `Some` |
| `pgcrypto` | `contrib/pgcrypto` | `Some` |
| `pg_stat_statements` | `contrib/pg_stat_statements` | `Some` (hooks/GUCs when preload) |
| `uuid-ossp` | `contrib/uuid_ossp` | `None` |
| `pg_prewarm` | `contrib/pg_prewarm` | `None` |
| `injection_points` | `contrib/injection_points` | `None` (+ injection callbacks) |
| `plpgsql` | `pl/plpgsql` handler | `Some` (GUCs / reserved prefix) |
| `dict_snowball` | `backend/snowball/dict_snowball` | `None` |

### Output plugins (related registry)

Logical decoding plugins use a parallel table:

- `BuiltinOutputPlugin { name, init, invoke }`
- `register_builtin_output_plugin` / `resolve_builtin_output_plugin` / `registry_invoke_builtin_output_plugin`
- Ported example: `test_decoding` (`contrib/test_decoding`) as name `"test_decoding"`

`load_output_plugin` consults the builtin table **before** the OS loader; per-change dispatch can pass a live `LogicalDecodingContext` into the registered `invoke`.

## Port pattern (contrib / regress)

Each ported loadable module:

1. Defines `const LIBRARY: &str = "…"` (simple name matching `$libdir/…`).
2. Implements fmgr-1 bodies as `PGFunction` / `LoadedExternalFunc { user_fn, api_version: 1 }`.
3. Implements `fn lookup(function: &str) -> Option<LoadedExternalFunc>` (match on `prosrc` symbol).
4. Optionally implements `fn pg_init() -> PgResult<()>` (C `_PG_init`).
5. In `pub fn init_seams()`:

```rust
pub fn init_seams() {
    dfmgr_seams::register_builtin_library(dfmgr_seams::BuiltinLibraryEntry {
        name: LIBRARY,
        lookup,
        pg_init: Some(pg_init), // or None
    });
}
```

6. Ensures `init` depends on the crate and calls `that_crate::init_seams()` from `init_all()`.

SQL and control files still use vendored extension scripts (`MODULE_PATHNAME` / `$libdir/...`); only the **body** is in-process. Unported modules remain unusable: no C ABI, OS `dlopen` path does not load arbitrary Postgres extensions.

`regress` additionally registers planner-support symbols by `prosrc` for symbols that are not fixed builtin OIDs (e.g. `test_support_func`).

## Verification

```bash
# Aggregator + double-install / wiring guards
cargo test -p init

# Full crate gate that also exercises the aggregator when --full
scripts/gate-crate --full <your-crate>

# Single crate under change
scripts/check-crate <your-crate>
cargo test -p <your-crate>
```

Runtime signals of miswiring:

| Symptom | Likely cause |
| --- | --- |
| `seam not installed: …` panic | Owner’s `init_seams` missing, not called from `init_all`, or call order never reached |
| `seam installed twice: …` panic | Two installers of the same slot; fails clean-process boot / `no_double_install` |
| `could not find function "…" in file "…"` | Library registered but symbol missing from `lookup` |
| OS / dlopen-style failure for `$libdir/foo` | Library name not registered (unported extension) |
| Extension SQL OK but hooks/GUCs missing | `pg_init` not registered or never run (needs load / first resolve path) |

## Constraints

- Seams are **not** a general plugin API for third-party crates; they are an internal cycle-break and install registry for the monorepo port.
- Builtin registration only covers libraries **ported into this tree** and wired into `init_all`. Generic Postgres extensions still require C ABI / `.so` load, which this backend does not provide.
- `init_all()` is intended **once per process** (OnceLock slots). Do not call it twice in the same process expecting reinstall.
- `register_builtin_library` is replace-by-name; double registration of the same name is harmless, unlike seam `set`.

## Related pages

<CardGroup>
  <Card title="Compatibility and status" href="/compatibility">
    Extension and PL boundaries that block generic Postgres extensions.
  </Card>
  <Card title="Use contrib extensions" href="/contrib-extensions">
    Ported contrib modules, CREATE EXTENSION, and unported gaps.
  </Card>
  <Card title="Process modes and dispatch" href="/process-modes">
    How the postgres binary dispatches after seam init.
  </Card>
  <Card title="Workspace and crate checks" href="/workspace-workflow">
    check-crate, gate-crate, and validating a crate plus the init aggregator.
  </Card>
  <Card title="Run regression tests" href="/regression-tests">
    Regression harness that depends on the in-process regress builtin.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Boot and extension-load failure modes.
  </Card>
</CardGroup>

---

## 09. Initialize a cluster

> Create PGDATA with postgres --initdb: supported flags, directory layout, bootstrap and post-bootstrap phases, and success signal text.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/09-initialize-a-cluster.md
- Generated: 2026-07-10T05:30:29.504Z

### Source Files

- `crates/backend/main/initdb/src/lib.rs`
- `crates/backend/main/main_main/src/lib.rs`
- `crates/_support/seam/init/src/bin/postgres.rs`
- `README.md`
- `vendor/postgres-18.3/share/pg_hba.conf.sample`
- `docker/entrypoint.sh`

---
title: "Initialize a cluster"
description: "Create PGDATA with postgres --initdb: supported flags, directory layout, bootstrap and post-bootstrap phases, and success signal text."
---

`postgres --initdb` (or `postgres initdb`) is pgrust’s built-in cluster bootstrap driver: the single `postgres` binary scaffolds a fresh `PGDATA`, writes config samples from the share directory, re-execs itself with `--boot` to load catalogs from `postgres.bki`, then re-execs with `--single template1` for post-bootstrap SQL and creation of `template0` and `postgres`. Unlike C PostgreSQL’s separate `initdb` binary, this path is an early intercept in `pg_main` (`MainOutcome::Initdb`), not a `DispatchOption`.

<Note>
Pass options as space-separated tokens (`--encoding UTF8`). Forms like `--encoding=UTF8` are not accepted and surface as unrecognized options.
</Note>

## Prerequisites

| Requirement | Detail |
| --- | --- |
| Binary | Release `postgres` from `cargo build --release --locked --bin postgres` (or the image’s `pgrust-postgres`) |
| Share tree | Vendored templates under `vendor/postgres-18.3/share` (or image `/usr/share/postgresql/18`) |
| `-L` override | Prefer explicit `-L` when the binary is not under a PGDG-style bindir; otherwise share path is derived via `get_share_path` from the executable path / bake-in `PGRUST_PGSHAREDIR` |
| Target dir | New path or empty directory; non-empty existing directories are rejected |
| Platform | Native only: wasm exits `1` for `MainOutcome::Initdb` (no subprocess re-exec) |

## Command

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
cargo build --release --locked --bin postgres

target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

Regression harness equivalent: `scripts/run-regression` runs the same shape (`--no-locale --encoding UTF8 -U postgres` with `-L` on the vendored share).

## Procedure

<Steps>
<Step title="Invoke the driver">
Call the same binary with `argv[1]` equal to `--initdb` or `initdb`. The entry shell installs seams, then `pg_main` returns `MainOutcome::Initdb` and the shell runs `initdb::initdb_main`.
</Step>
<Step title="Scaffold PGDATA">
Create the data directory (mode `0700` on Unix), the fixed subdirectory tree, and top-level `PG_VERSION` containing `18`.
</Step>
<Step title="Write configuration files">
Copy and substitute share samples into `postgresql.conf`, `pg_hba.conf`, `pg_ident.conf`, and create empty `postgresql.auto.conf`.
</Step>
<Step title="Bootstrap template1">
Substitute tokens in `postgres.bki` and pipe the result to a child:

`postgres --boot -F -c log_checkpoints=false -X <wal_seg_bytes> -D <pgdata>`

Then write `base/1/PG_VERSION`.
</Step>
<Step title="Post-bootstrap SQL">
Pipe the assembled SQL stream to:

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

This installs system catalogs extras, privileges, `information_schema`, `plpgsql`, freezes the cluster, and creates `template0` / `postgres`.
</Step>
</Steps>

## Supported options

| Flag | Type | Default | Behavior |
| --- | --- | --- | --- |
| `-D` / `--pgdata` | path | *required* | Target data directory; also accepts glued `-D/path` |
| `-U` / `--username` | string | `$USER`, else `postgres` | Bootstrap superuser name (BKI `POSTGRES` token) |
| `-L` | path | `get_share_path(current_exe)` | Share directory for samples, BKI, and SQL files |
| `-E` / `--encoding` | name | `UTF8` (id `6`) | Server encoding: `SQLASCII`/`UTF8`/`UNICODE`/`LATIN1` only |
| `--lc-collate` | string | `C` | Collation locale token for BKI |
| `--lc-ctype` | string | `C` | Ctype locale token for BKI |
| `--locale` | string | — | Accepted; value is discarded; only `C` is effectively honored |
| `--no-locale` | flag | — | Forces collate/ctype to `C` |
| `--wal-segsize` | integer MB | `16` | WAL segment size in megabytes (converted to bytes for `-X`) |

Any other flag fails with `unrecognized initdb option "…"`.

### Encodings

| Name | Encoding id |
| --- | --- |
| `SQLASCII` | `0` |
| `UTF8`, `UNICODE` | `6` |
| `LATIN1` | `8` |

Other names fail with `unsupported encoding "…"`.

### Explicitly unsupported C initdb options

These are common upstream flags that pgrust rejects (or never implements). Docker documents the same subset:

- Auth: `-A`, `--auth`, `--auth-local`, `--auth-host` (host/local auth is hard-coded to `trust` in generated `pg_hba.conf`)
- Password: `--pwfile`, `-W` / `--pwprompt` (Docker sets the password later with `ALTER ROLE`)
- Storage: `--data-checksums`, `--waldir` as a first-class initdb flag (entrypoint may pass `--waldir` via `POSTGRES_INITDB_ARGS`, which will fail unless you avoid it)
- Locale/auth extras and other C-only options: any token not in the table above

## Progress and success output

On success, stderr looks like:

```text
The files belonging to this database system will be owned by user "<username>".
The database cluster will be initialized with locale "C".

creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok

Success. You can now start the database server using pgrust/postgres -D <pgdata>
```

| Signal | Meaning |
| --- | --- |
| `creating configuration files ... ok` | Samples written under `PGDATA` |
| `running bootstrap script ... ok` | `--boot` child exited 0; `base/1/PG_VERSION` written |
| `performing post-bootstrap initialization ... ok` | `--single` child exited 0 |
| Final `Success. …` line | Cluster is ready for postmaster start |
| Exit code `0` | Driver finished cleanly |
| `initdb: error: …` on stderr, exit `1` | Any phase failed |

## Directory layout

Created under `PGDATA` (`SUBDIRS` plus version/config files):

:::files
PGDATA/
  PG_VERSION                 # "18\n"
  postgresql.conf            # from postgresql.conf.sample
  postgresql.auto.conf       # ALTER SYSTEM header only
  pg_hba.conf                # from sample; local/host methods = trust
  pg_ident.conf              # from pg_ident.conf.sample
  global/
  base/
    1/                       # template1 OID directory
      PG_VERSION             # written after --boot
  pg_wal/
    archive_status/
    summaries/
  pg_xact/
  pg_commit_ts/
  pg_dynshmem/
  pg_notify/
  pg_serial/
  pg_snapshots/
  pg_subtrans/
  pg_twophase/
  pg_multixact/
    members/
    offsets/
  pg_replslot/
  pg_tblspc/
  pg_stat/
  pg_stat_tmp/
  pg_logical/
    snapshots/
    mappings/
:::

After post-bootstrap, catalogs include databases `template1`, `template0` (OID `4`, no connections, is template), and `postgres` (OID `5`).

## Share directory inputs

`-L` (or derived share path) must contain at least:

| File | Role |
| --- | --- |
| `postgresql.conf.sample` | Copied to `postgresql.conf` |
| `pg_hba.conf.sample` | Token-substituted to `pg_hba.conf` |
| `pg_ident.conf.sample` | Copied to `pg_ident.conf` |
| `postgres.bki` | Catalog bootstrap; first line must be `# PostgreSQL 18` |
| `system_constraints.sql` | Post-bootstrap |
| `system_functions.sql` | Post-bootstrap |
| `system_views.sql` | Post-bootstrap |
| `snowball_create.sql` | Text-search dictionaries |
| `information_schema.sql` | information_schema |
| `sql_features.txt` | `COPY` into `information_schema.sql_features` |

### `pg_hba.conf` substitutions

| Token | Value used |
| --- | --- |
| `@authmethodlocal@` | `trust` |
| `@authmethodhost@` | `trust` |
| `@authcomment@` | Local trust caution comment (`AUTHTRUST_WARNING`) |
| `@remove-line-for-nolocal@` | Empty (local lines kept) |

No `-A` path exists; local and host sample lines always use `trust`.

## Bootstrap phase (`--boot`)

1. Read `{sharedir}/postgres.bki`.
2. Reject BKI if the first line is not `# PostgreSQL 18`.
3. Substitute BKI tokens:

| Token | Substitution |
| --- | --- |
| `NAMEDATALEN` | `64` |
| `SIZEOF_POINTER` | Host pointer size |
| `ALIGNOF_POINTER` | `i` (32-bit) or `d` (64-bit) |
| `FLOAT8PASSBYVAL` | `true` on 64-bit, else `false` |
| `POSTGRES` | Escaped superuser name |
| `ENCODING` | Encoding id string |
| `LC_COLLATE` / `LC_CTYPE` | Escaped locale strings |
| `DATLOCALE` / `ICU_RULES` | `_null_` |
| `LOCALE_PROVIDER` | `c` |

4. Spawn `current_exe` with stdin = substituted BKI:

```text
--boot -F -c log_checkpoints=false -X <wal_segment_size_bytes> -D <pgdata>
```

Non-zero child status → `bootstrap backend exited with …`.

## Post-bootstrap phase (`--single`)

One standalone backend receives a concatenated SQL stream (order matches C’s `setup_*` sequence):

1. `REVOKE ALL ON pg_authid FROM public`
2. Inline `system_constraints.sql`, `system_functions.sql`
3. `SELECT pg_stop_making_pinned_objects()`
4. Inline `system_views.sql`
5. Operator description inserts, collation version update, `pg_import_system_collations`
6. Inline `snowball_create.sql`
7. Default privileges + `pg_init_privs` population
8. Inline `information_schema.sql`, set DBMS version `18.00.0000`, `COPY` `sql_features.txt`
9. `CREATE EXTENSION plpgsql`
10. `ANALYZE`; `VACUUM FREEZE`
11. Create `template0` / lock down template DBs; create `postgres`

Child argv:

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

<Info>
C initdb also passes `-c exit_on_error=true`. pgrust omits that GUC because single-user mode would FATAL with “there is no client connection”. SQL failures still surface on the child stderr and as a non-zero child exit.
</Info>

## Failure modes

| Error text / condition | Cause |
| --- | --- |
| `no data directory specified (use -D / --pgdata)` | Missing `-D` |
| `directory "…" exists but is not empty` | Non-empty target |
| `unrecognized initdb option "…"` | Unsupported or `=`-joined flag |
| `unsupported encoding "…"` | Encoding outside the wired set |
| `option … requires an argument` | Flag without following value |
| `could not read "…/postgres.bki"` / sample / SQL | Wrong or incomplete `-L` share tree |
| BKI header mismatch | Share tree not major version `18` |
| `bootstrap backend exited with …` | `--boot` failed (catalogs/share/backend) |
| `post-bootstrap backend exited with …` | `--single` SQL/catalog failure |
| `initdb: error: …` (wrapper) | Any `initdb_main` `Err` printed by the binary shell |

After a failed run, remove the partial `PGDATA` before retrying.

## Docker first-boot

The image entrypoint (`docker/entrypoint.sh`) calls the same driver when `$PGDATA/PG_VERSION` is absent:

```bash
"$PGRUST_BIN" --initdb -D "$PGDATA" -L "$PG_SHAREDIR" --username "$POSTGRES_USER" $POSTGRES_INITDB_ARGS
```

Defaults: `PGRUST_BIN=/usr/local/bin/pgrust-postgres`, share via `PG_SHAREDIR` / `PGRUST_PGSHAREDIR` (default `/usr/share/postgresql/18`). Superuser password is applied after a temporary server start (`ALTER ROLE`), not via `--pwfile`. Only initdb options from the supported table may appear in `POSTGRES_INITDB_ARGS`.

## After initdb

Start the postmaster with the same data directory. Runtime still requires pgrust-specific stack and I/O settings (for example `io_method=sync`, large `max_stack_depth` / `RUST_MIN_STACK`, and `ulimit -s`). See run-server and configuration reference.

## Related pages

<CardGroup>
<Card title="initdb options reference" href="/initdb-reference">
Full argv surface: space-separated parsing, defaults, and unsupported options that error.
</Card>
<Card title="Install paths and share directory" href="/install-paths">
`PGRUST_PGSHAREDIR`, runtime `-L`, and why a wrong share tree breaks bootstrap.
</Card>
<Card title="Process modes and dispatch" href="/process-modes">
How `--initdb`, `--boot`, `--single`, and postmaster share one binary.
</Card>
<Card title="Run the server" href="/run-server">
Start postmaster on the new PGDATA with required GUCs and connect with psql.
</Card>
<Card title="Quickstart" href="/quickstart">
Shortest local path: build, initdb, start, verify `version()`.
</Card>
<Card title="Docker" href="/docker">
First-boot init via the official-shaped entrypoint and image env vars.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Initdb option gaps, share/tz path failures, and related boot issues.
</Card>
</CardGroup>

---

## 10. Run the server

> Start postmaster with -D, socket/TCP flags, mandatory io_method=sync and stack settings, connect with psql, and confirm readiness.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/10-run-the-server.md
- Generated: 2026-07-10T05:31:05.417Z

### Source Files

- `README.md`
- `scripts/run-regression`
- `docker/entrypoint.sh`
- `crates/backend/postmaster/postmaster/src/main_entry.rs`
- `crates/backend/main/main_main/src/help.rs`
- `crates/backend/storage/buffer/bufmgr/src/buf_aio.rs`

---
title: "Run the server"
description: "Start postmaster with -D, socket/TCP flags, mandatory io_method=sync and stack settings, connect with psql, and confirm readiness."
---

With no dispatch-mode flag first (`--single`, `--boot`, `--check`, `--initdb`), `target/release/postgres` enters postmaster mode: `PostmasterMain` validates `PGDATA`, binds TCP and Unix sockets from `listen_addresses` / `unix_socket_directories`, forks checkpointer/bgwriter/startup, then blocks in `ServerLoop`. Operational boots used in the README, Docker entrypoint, and `scripts/run-regression` all pass `-c io_method=sync` and raise process/Rust/GUC stack limits before accepting clients.

<Warning>
pgrust is not production-ready. Always pass `-c io_method=sync` for local and Docker runs. The compiled default `io_method` is `worker` (`DEFAULT_IO_METHOD`); selecting `io_uring` panics as unported. Buffer-manager AIO read paths are documented for the synchronous method.
</Warning>

## Prerequisites

| Requirement | Detail |
| --- | --- |
| Binary | `target/release/postgres` from a release build, or the Docker image binary |
| Data directory | Existing cluster with `global/pg_control` (create with `postgres --initdb` first) |
| Non-root OS user | `check_root` refuses effective uid 0 and mismatched real/effective uids |
| Client | PostgreSQL 18 `psql` on `PATH` (or `PGRUST_PSQL` for harnesses) |
| Stack headroom | `ulimit -s 65520` and `RUST_MIN_STACK=33554432` before start |

## Mandatory runtime settings

These values appear in README start instructions, `docker/entrypoint.sh` (`PGRUST_SERVER_OPTS`), and `scripts/run-regression`.

| Setting | Value | Role |
| --- | --- | --- |
| Shell `ulimit -s` | `65520` | Raise OS stack so deep backend frames do not SIGSEGV |
| `RUST_MIN_STACK` | `33554432` (32 MiB) | Rust thread stack floor for the server process |
| `-c io_method` | `sync` | Force synchronous AIO method (overrides boot default `worker`) |
| `-c max_stack_depth` | `60000` (kB) | GUC stack budget used by harnesses/Docker (process default is `2048` kB) |

```bash
ulimit -s 65520
export RUST_MIN_STACK=33554432
```

<Note>
`max_stack_depth` process default is intentionally `2048` (not C’s `100`) because Rust frames plus seam indirection deepen call chains. Harnesses still raise it further to `60000` / `7000` depending on path.
</Note>

## Start postmaster

### Recommended local command

Socket-only (no TCP), fsync off, short Unix socket path — matches the documented source-build path:

```bash
ulimit -s 65520

RUST_MIN_STACK=33554432 target/release/postgres \
  -D /tmp/pgrust-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000
```

### Flag map

| Flag / option | Sets | Notes |
| --- | --- | --- |
| `-D DATADIR` | Data directory | Required; postmaster selects config, chdirs, lockfile |
| `-k DIRECTORY` | `unix_socket_directories` | Default GUC boot value is `/tmp` |
| `-p PORT` | `port` | Default client/server port is `5432` |
| `-h HOSTNAME` | `listen_addresses` | TCP bind list |
| `-i` | `listen_addresses=*` | Deprecated enable-TCP form |
| `-c listen_addresses=` | Empty TCP list | Socket-only; common for local/dev |
| `-c listen_addresses=*` | All interfaces | Docker final server injects this |
| `-F` | `fsync=false` | Dev convenience; not for durable production |
| `-c NAME=VALUE` / `--NAME=VALUE` | Any GUC | Postmaster context for most boot-time settings |
| `-c io_method=sync` | AIO method | **Required** for the supported operational path |
| `-c max_stack_depth=60000` | Stack GUC | Matches Docker / regression |

### Regression harness start line

`scripts/run-regression` backgrounds the same shape with a short socket dir under `/tmp` (macOS `sun_path` length) and extra GUCs:

```bash
"$PGRUST_BIN" -D "$DATADIR" -F -c listen_addresses= -k "$SOCK" -p "$PORT" \
  -c io_method=sync -c max_stack_depth=60000 \
  -c checkpoint_timeout=3600 -c autovacuum=on \
  -c max_logical_replication_workers=0
```

Default harness port is `55434` (`PGPORT`).

### Docker final exec

After first-boot init, the entrypoint rewrites `postgres …` to the pgrust binary and always injects:

```text
-c io_method=sync
-c max_stack_depth=60000
-c unix_socket_directories=/var/run/postgresql
-c listen_addresses='*'
```

Temp init servers use the same `PGRUST_SERVER_OPTS` with `listen_addresses=''` (socket-only).

## What PostmasterMain does

```text
argv (default mode)
        │
        ▼
  check_root (refuse uid 0)
        │
        ▼
  PostmasterMain
   ├─ InitProcessGlobals, signal handlers, GUC init
   ├─ process_postgres_switches (-D, -k, -p, -c …)
   ├─ SelectConfigFiles + checkDataDir + pg_control present
   ├─ CreateSharedMemoryAndSemaphores
   ├─ establish_input_sockets
   │     listen_addresses  → TCP ListenServerPort
   │     unix_socket_directories → AF_UNIX ListenServerPort
   ├─ PM_STARTUP + checkpointer / bgwriter / startup children
   └─ ServerLoop()  (never returns)
              │
              ▼  (after recovery)
   log: "database system is ready to accept connections"
```

Missing every listen socket is fatal: `no socket created for listening`. Missing `global/pg_control` exits after a diagnostic naming the expected path.

## Connect with psql

### Unix socket (local recommended)

```bash
psql -h /tmp -p 5432 -U postgres -d postgres \
  -c "select version(), 1 + 1 as two"
```

Match `-h` to the directory from `-k` / `unix_socket_directories`.

### TCP

Only if the server listens on TCP (`listen_addresses` non-empty) and `pg_hba.conf` allows the method:

```bash
psql -h 127.0.0.1 -p 5432 -U postgres -d postgres
```

<Warning>
Docker defaults `POSTGRES_HOST_AUTH_METHOD` to `trust` for host lines because password authentication (`fetch_role_password`) is not fully ported. Forcing scram/md5 host methods yields FATAL seam errors on TCP logins even with a correct password. Local/unix `trust` remains the reliable path for clients.
</Warning>

## Confirm readiness

<Steps>
  <Step title="Process still alive">
    After backgrounding the postmaster, ensure the PID has not exited. Harnesses treat early death as boot failure and print the tail of the postmaster log.
  </Step>
  <Step title="Poll a trivial query">
    Readiness is defined by accepting a successful SQL session, not by a separate control protocol:

```bash
psql -h /tmp -p 5432 -U postgres -d postgres -c 'SELECT 1'
```

    Regression waits up to ~80s (`160 × 0.5s`). Docker temp-server start polls up to 120 iterations of 0.5s against the Unix socket under `/var/run/postgresql`.
  </Step>
  <Step title="Optional log line">
    After recovery completes, the reaper path logs:

```text
database system is ready to accept connections
```

    Hot-standby promotion may log readiness for read-only connections instead.
  </Step>
  <Step title="Smoke SQL">
```bash
psql -h /tmp -p 5432 -U postgres -d postgres \
  -c "select version(), 1 + 1 as two"
```
  </Step>
</Steps>

<Check>
A non-zero `psql` exit, connection refused, or “postmaster died during boot” means the cluster is not ready — inspect the postmaster stderr/log for GUC, share-dir, stack, or control-file errors.
</Check>

## Stop the server

| Signal | Effect (as used in-repo) |
| --- | --- |
| `SIGINT` | Fast shutdown path used by Docker temp-server stop |
| `SIGTERM` / `SIGQUIT` | Postmaster shutdown handlers (same signal set as C postmaster) |
| Harness cleanup | `kill` then escalate to `kill -9` if the PID lingers |

There is no separate `pg_ctl` wrapper in the clone-only path; send signals to the postmaster PID directly.

## Failure modes at start

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Boot fails / AIO issues without `io_method=sync` | Default `worker` or other method not suitable for current operational path | Always pass `-c io_method=sync` |
| Panic mentioning `io_uring` | Unsupported method selected | Use `sync` only |
| Stack overflow / “stack depth limit exceeded” | Low OS stack, missing `RUST_MIN_STACK`, or low `max_stack_depth` | `ulimit -s 65520`, `RUST_MIN_STACK=33554432`, `-c max_stack_depth=60000` |
| `"root" execution of the PostgreSQL server is not permitted` | Started as uid 0 | Run as an unprivileged user (Docker uses `gosu postgres`) |
| `could not find the database system: expected to find pg_control at …` | Empty or wrong `-D` | Init with `--initdb` or point `-D` at a valid PG 18.3 data dir |
| `no socket created for listening` | Empty/failed `listen_addresses` and `unix_socket_directories` | Set `-k` / Unix dirs and/or TCP listen list |
| Unix socket connect fails on macOS | Socket path longer than `sun_path` | Use a short `-k` directory under `/tmp` (as regression does) |
| Wrong timezone / share files | Share tree not baked or wrong at init | Build with `PGRUST_PGSHAREDIR`; init with `-L` to vendored share |
| TCP auth FATAL `fetch_role_password` | Password host auth unported | Prefer unix socket or `trust` host method |

## Related pages

<CardGroup cols={2}>
  <Card title="Initialize a cluster" href="/init-cluster">
    Create PGDATA with `postgres --initdb` before first start.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `io_method`, stack GUCs, listen settings, and build-time path env vars.
  </Card>
  <Card title="postgres CLI reference" href="/cli-reference">
    Full `--help` flags and GUC override forms.
  </Card>
  <Card title="Docker" href="/docker">
    Image entrypoint injects the same mandatory opts and readiness polling.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Stack, share path, root, and missing-client failures in one place.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    Shortest build → init → start → `version()` path.
  </Card>
</CardGroup>

---

## 11. Boot from existing Postgres data

> Constraints for reusing a Postgres 18.3 PGDATA: control file checks, major-version expectations, and practical recovery when byte order or catalog state does not match.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/11-boot-from-existing-postgres-data.md
- Generated: 2026-07-10T05:30:55.567Z

### Source Files

- `README.md`
- `crates/common/controldata_utils/src/lib.rs`
- `crates/backend/postmaster/postmaster/src/main_entry.rs`
- `crates/backend/main/initdb/src/lib.rs`
- `scripts/run-recovery-tap`

---
title: "Boot from existing Postgres data"
description: "Constraints for reusing a Postgres 18.3 PGDATA: control file checks, major-version expectations, and practical recovery when byte order or catalog state does not match."
---

pgrust claims on-disk compatibility with PostgreSQL 18.3: the same `postgres` binary can start postmaster against a data directory created by C PostgreSQL 18.3 or by `postgres --initdb`, as long as `PG_VERSION`, `global/pg_control`, and the compile-time layout constants match what this server was built for. Boot does not rewrite an incompatible cluster; validation is fail-closed.

<Warning>
pgrust is not production-ready. Reusing a real Postgres 18.3 data directory is for development, recovery experiments, and regression-style verification—not a supported production cutover path.
</Warning>

## Compatibility contract

| Surface | Expected value / behavior |
| --- | --- |
| Major version in `PGDATA/PG_VERSION` | Major `18` (initdb writes `18`; server compares major of `18.3`) |
| Control file | `PGDATA/global/pg_control` present, fixed-layout image |
| `PG_CONTROL_VERSION` | `1800` |
| `CATALOG_VERSION_NO` | `202506291` (PostgreSQL 18.3) |
| Tablespace version dir name | `PG_18_202506291` |
| Host endian / struct layout | Native-endian decode of control fields; wrong endian fails control checks |
| Extensions / PLs | Generic C extensions and PL/Python, PL/Perl, PL/Tcl are not generally usable even if the on-disk cluster is compatible |

There is no `pg_upgrade` workflow in this repository. Wrong major, wrong catalog version, wrong byte order, or mismatched compile-time sizes require a cluster that already matches 18.3 layout, or a fresh `--initdb`.

## Prerequisites

- Built `target/release/postgres` (or image `malisper/pgrust`) with share path wiring that can still serve config samples if you re-init; for boot-only of an existing tree, share path matters less than for initdb, but runtime `-c` settings still apply.
- A PostgreSQL **18.x** data directory (major `18`), ideally from **18.3**, cleanly shut down when possible.
- Same process user as the directory owner (ownership check is fatal).
- Directory mode without group-write or world bits (allowed modes effectively `0700` / `0750`).
- Mandatory runtime GUCs when starting pgrust: `io_method=sync`, adequate stack (`ulimit -s`, `RUST_MIN_STACK`, `max_stack_depth`).

## Boot sequence against existing `PGDATA`

Postmaster startup validates the data directory before accepting connections:

```text
SelectConfigFiles (-D / config)
        │
        v
 checkDataDir ──► exists, is dir, owner=geteuid, mode ok
        │
        v
 ValidatePgVersion ──► major from PG_VERSION == major of "18.3"
        │
        v
 check_control_file ──► open PGDATA/global/pg_control (existence only)
        │
        v
 ChangeToDataDir + CreateDataDirLockFile
        │
        v
 LocalProcessControlFile → ReadControlFile
        │                 (version, CRC, catalog, layout fields, WAL seg size)
        v
 continue postmaster init (preload, shmem, …)
```

### Existence-only control check

Before full parse, postmaster confirms `global/pg_control` is openable. Missing file exits with a log-style diagnostic and postmaster exit code `2`:

```text
could not find the database system: expected to find pg_control at "<DataDir>/global/pg_control"
```

Full CRC and compatibility validation happens in `ReadControlFile`, not in this existence probe.

## `PG_VERSION` major check

`ValidatePgVersion` reads the first whitespace-delimited token from `PGDATA/PG_VERSION` (max 63 characters) and compares its leading integer major to the server’s major from `PG_VERSION` (`"18.3"` → major `18`).

| Condition | Outcome |
| --- | --- |
| Missing `PG_VERSION` | FATAL: `"… is not a valid data directory"` |
| Empty / non-numeric token | Same invalid-directory FATAL |
| Major ≠ server major | FATAL: `database files are incompatible with server` |
| Major matches (`18`) | Proceed |

pgrust initdb writes top-level `PG_VERSION` as `18` (major only), matching C initdb major-file semantics. Minor differences within major 18 are not rejected by this check; catalog and control fields still must match.

## Control file layout and readers

On-disk `global/pg_control` is zero-padded to `PG_CONTROL_FILE_SIZE` (8192). Readers take the leading `ControlFileData` image (`SIZE_OF_CONTROL_FILE_DATA` = 296 bytes on LP64, CRC at offset 292). Fields are decoded with **native endian** integers.

Two readers share the image but differ in severity:

| Reader | Used for | CRC mismatch | `PG_CONTROL_VERSION` / byte order |
| --- | --- | --- | --- |
| `get_controlfile` (`controldata_utils`) | SQL `pg_control_*` helpers | Returns `crc_ok=false` (caller may error) | Hard ERROR; byte-order heuristic then exact version |
| `ReadControlFile` (startup via `LocalProcessControlFile`) | Postmaster / backend boot | Hard error: `incorrect checksum in control file` | Hard error: incompatible control version |

### Byte-order heuristic (`get_controlfile`)

If `pg_control_version % 65536 == 0` and `pg_control_version / 65536 != 0`, the server treats the image as **wrong endian** for this host:

```text
byte ordering mismatch in control file "<DataDir>/global/pg_control"
detail: Possibly byte ordering doesn't match the database server that wrote this control file.
```

Otherwise, if `pg_control_version != PG_CONTROL_VERSION` (`1800`):

```text
the database cluster was initialized with PG_CONTROL_VERSION … but the server was compiled with PG_CONTROL_VERSION 1800 (0x00000708)
hint: This could be a problem of mismatched byte ordering.  It looks like you need to initdb.
```

### Startup compatibility fields (`ReadControlFile`)

After requiring `pg_control_version == 1800` and a matching CRC, startup equality-checks compile-time layout constants:

| Control field | Server expectation |
| --- | --- |
| `catalog_version_no` | `202506291` |
| `maxAlign` | `8` |
| `floatFormat` | `1234567.0` (`FLOATFORMAT_VALUE`) |
| `blcksz` | `8192` |
| `relseg_size` | `131072` |
| `xlog_blcksz` | matches `XLOG_BLCKSZ` |
| `nameDataLen` | `64` |
| `indexMaxKeys` | `32` |
| `toast_max_chunk_size` | `1996` |
| `loblksize` | `2048` |
| `float8ByVal` | `true` |
| `xlog_seg_size` | Valid WAL segment size (power-of-two range as in Postgres) |

Mismatch shape:

```text
database files are incompatible with server: <NAME> <on-disk> but server <expected>
```

Special cases: float format → `float format mismatch`; float8 by-value → `USE_FLOAT8_BYVAL mismatch`; bad WAL segment size → `invalid WAL segment size in control file (… bytes)`.

## Start an existing data directory

Same command shape as a pgrust-created cluster; only `-D` points at the foreign tree.

```bash
ulimit -s 65520

RUST_MIN_STACK=33554432 target/release/postgres \
  -D /path/to/pg18-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000
```

Verify:

```bash
psql -h /tmp -p 5432 -U postgres -d postgres \
  -c "select version(), current_setting('server_version_num')"
```

`version()` is branded `pgrust 18.3 …`; `server_version_num` remains `180003`. A successful connect after the control checks is the readiness signal for an existing tree.

### Docker volume reuse

The Docker entrypoint treats a non-empty `PG_VERSION` under `PGDATA` as `DATABASE_ALREADY_EXISTS` and skips `--initdb`. Point the volume at a compatible 18.x tree and start the image as usual; first-boot init scripts under `/docker-entrypoint-initdb.d` run only when the directory was empty.

## Practical recovery when validation fails

pgrust does **not** auto-repair control files, byte-swap catalogs, or upgrade majors. Recovery is operational:

| Failure | Practical action |
| --- | --- |
| Wrong major (`PG_VERSION` not 18) | Use a true PG 18 cluster, or create a new one with `postgres --initdb` |
| Byte-order / wrong-endian control file | Run pgrust on a host with the **same endianness** as the writer, or rebuild data on this host |
| `PG_CONTROL_VERSION` ≠ 1800 | Cluster is not PG 18 control format; re-init or use a matching 18.x cluster |
| `CATALOG_VERSION_NO` ≠ `202506291` | Catalog is not the 18.3 catalog this binary expects; re-init with this binary or with matching C 18.3 |
| Layout field mismatch (BLCKSZ, MAXALIGN, …) | Binary and data were not built with the same compile-time sizes; use matching builds or re-init |
| Incorrect checksum | Corrupt or partially written control file; restore from backup or re-init |
| Missing `global/pg_control` | Incomplete data directory; restore or re-init |
| `directory … exists but is not empty` from `--initdb` | initdb refuses non-empty targets; use a new path or remove/rename the old tree after backup |

### Fresh cluster when reuse is impossible

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data-new \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

Success ends with:

```text
Success. You can now start the database server using pgrust/postgres -D …
```

Do not run `--initdb` into a live production path without an explicit backup and empty (or new) directory.

## Cross-engine verification (recovery TAP)

`scripts/run-recovery-tap` is the in-repo proof pattern that a **C-initdb’d** cluster can be driven by the **pgrust** postmaster: the harness fakes an install where `initdb` / `pg_ctl` / client tools come from a C PostgreSQL install, while `postgres` is a real copy of the pgrust binary. It injects:

```text
io_method = sync
max_stack_depth = 7000kB
```

via `TEMP_CONFIG` so TAP nodes start under pgrust constraints. That path exercises archive recovery and related suites against on-disk state produced by C tools—not a guarantee for every production extension or custom build option.

## Constraints that still apply after a successful control check

- **IO method**: default worker IO is unported; without `io_method=sync` the server may fail early.
- **Stack**: large frames need raised `ulimit -s`, `RUST_MIN_STACK`, and `max_stack_depth`.
- **Auth / extensions**: Docker notes that password host auth can still fail until password seams are complete; unported extensions will not load even if the cluster catalogs reference them.
- **Ownership / permissions**: wrong owner or overly open directory modes fail before control parse.
- **Lock file**: only one postmaster per data directory; existing `postmaster.pid` from a crashed host may need the usual Postgres cleanup after confirming no live process.

## Failure signals (quick map)

| Symptom | Likely stage |
| --- | --- |
| `data directory "…" does not exist` / not a directory | `checkDataDir` |
| `has wrong ownership` / `invalid permissions` | `checkDataDir` |
| `is not a valid data directory` | Missing or unreadable `PG_VERSION` |
| `database files are incompatible with server` (no field name) | Major version mismatch |
| `could not find the database system: expected to find pg_control` | Existence probe |
| `byte ordering mismatch` / `need to initdb` | Control version / endian |
| `incorrect checksum in control file` | CRC at startup |
| `CATALOG_VERSION_NO … but server 202506291` | Catalog epoch mismatch |
| Panic / early exit mentioning `io_method` or stack | Runtime GUC / process limits after control accepted |

## Next

<CardGroup>
  <Card title="Initialize a cluster" href="/init-cluster">
    Create a matching PGDATA with postgres --initdb when reuse is not possible.
  </Card>
  <Card title="Run the server" href="/run-server">
    Required -D, socket/TCP, io_method=sync, and stack settings for a successful postmaster start.
  </Card>
  <Card title="Compatibility and status" href="/compatibility">
    On-disk compatibility claims, non-production status, and extension boundaries.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Known failure modes including stack, io_method, share path, and root refusal.
  </Card>
  <Card title="Test harness reference" href="/test-harness-reference">
    Environment for recovery TAP and other harnesses that mix C tools with pgrust.
  </Card>
</CardGroup>

---

## 12. Use contrib extensions

> Ported contrib modules registered as builtins (citext, hstore, ltree, pg_trgm, pgcrypto, pg_stat_statements, uuid-ossp, and others), CREATE EXTENSION against vendored control/SQL, and what remains unported.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/12-use-contrib-extensions.md
- Generated: 2026-07-10T05:31:49.379Z

### Source Files

- `crates/contrib/hstore/src/lib.rs`
- `crates/contrib/pg_stat_statements/src/lib.rs`
- `crates/contrib/pg_trgm/src/lib.rs`
- `crates/contrib/pgcrypto/src/lib.rs`
- `crates/backend/utils/fmgr/dfmgr_seams/src/lib.rs`
- `vendor/postgres-18.3/share/extension/hstore.control`
- `crates/_support/seam/init/src/lib.rs`

---
title: "Use contrib extensions"
description: "Ported contrib modules registered as builtins (citext, hstore, ltree, pg_trgm, pgcrypto, pg_stat_statements, uuid-ossp, and others), CREATE EXTENSION against vendored control/SQL, and what remains unported."
---

pgrust installs contrib and PL modules through the same SQL surface as Postgres (`CREATE EXTENSION`, `LOAD`, `shared_preload_libraries`), but ported libraries never load as `.so` files. Each ported module registers a simple library name (`hstore`, `pgcrypto`, `uuid-ossp`, …) into the in-process builtin registry owned by `dfmgr_seams`; `load_external_function` / `load_file` resolve `$libdir/<name>` against that registry before any OS `dlopen`. Extension control files and install SQL live under the share tree’s `extension/` directory (clone default: `vendor/postgres-18.3/share/extension/`).

```mermaid
flowchart LR
  subgraph share["Share tree"]
    CTL["name.control\nmodule_pathname=$libdir/name"]
    SQL["name--version.sql\nCREATE FUNCTION … LANGUAGE C AS MODULE_PATHNAME"]
  end
  subgraph sql["SQL / utility"]
    CE["CREATE EXTENSION name"]
    LOAD["LOAD 'name' /\nshared_preload_libraries"]
  end
  subgraph runtime["postgres binary"]
    SEAMS["seams-init::init_all()\n→ crate::init_seams()"]
    REG["dfmgr_seams\nBUILTIN_LIBRARIES"]
    DFMGR["fmgr_dfmgr\nsimple_library_name + resolve"]
  end
  SEAMS --> REG
  CTL --> CE
  SQL --> CE
  CE --> DFMGR
  LOAD --> DFMGR
  DFMGR --> REG
```

## How loading works

1. At process startup, `seams-init` calls each ported crate’s `init_seams()`, which calls `dfmgr_seams::register_builtin_library` with a `BuiltinLibraryEntry` (`name`, symbol `lookup`, optional `pg_init`).
2. `CREATE EXTENSION` reads `<name>.control` from the extension control path (default: `$share/extension`), then runs the version SQL script with `MODULE_PATHNAME` substituted from `module_pathname` (normally `$libdir/<name>`).
3. Each `CREATE FUNCTION … LANGUAGE C AS 'MODULE_PATHNAME','sym'` call reaches `load_external_function`, which strips path/suffix to a simple name and hits the registry when present.
4. Optional `_PG_init` equivalents (`pg_init`) run once per backend on first load (`LOAD`, preload, or first symbol resolve)—for example pgcrypto’s `pgcrypto.builtin_crypto_enabled` GUC and `pg_stat_statements` hooks when preload is in progress.

Unregistered names fall through to the OS dynamic loader. The Rust backend does not expose a Postgres C ABI for arbitrary extensions, so generic third-party `.so` modules are not a supported path.

## Prerequisites

| Requirement | Detail |
| --- | --- |
| Running server | Built `postgres` binary with share dir wired (`PGRUST_PGSHAREDIR` at build, or `-L` at `--initdb` / boot). |
| Share tree | Must contain `extension/<name>.control` and matching `*.sql` scripts. Clone tree: `vendor/postgres-18.3/share`. |
| Superuser / trust | Same as Postgres control flags (`superuser`, `trusted`). |
| `pg_stat_statements` only | Must appear in `shared_preload_libraries` **before** postmaster start, then `CREATE EXTENSION`. |

`plpgsql` is installed automatically during `--initdb` (`CREATE EXTENSION plpgsql` in the post-bootstrap SQL). You do not need to create it by hand on a fresh cluster.

## Create a ported extension

<Steps>
<Step title="Confirm the share tree">
Point the cluster at a share directory that includes the extension control/SQL files. On a source build:

```bash
ls vendor/postgres-18.3/share/extension/*.control
```

Wrong or incomplete share trees cause missing-control or missing-script failures before any symbol load.
</Step>
<Step title="Connect and create">
```sql
CREATE EXTENSION hstore;
CREATE EXTENSION citext;
CREATE EXTENSION pg_trgm;
CREATE EXTENSION pgcrypto;
CREATE EXTENSION ltree;
CREATE EXTENSION "uuid-ossp";
```

Quoted identifiers matter for hyphenated names (`uuid-ossp`). Default versions come from each `.control` file (for example hstore `1.8`, citext `1.8`, pg_trgm `1.6`, pgcrypto `1.4`, ltree `1.3`, uuid-ossp `1.1`).
</Step>
<Step title="Verify">
```sql
\dx
SELECT 'a' || hstore('k','v');  -- after CREATE EXTENSION hstore
SELECT similarity('cat', 'cats');  -- after CREATE EXTENSION pg_trgm
SELECT digest('x', 'sha256');  -- after CREATE EXTENSION pgcrypto
```

`\dx` lists installed extensions. Function calls that resolve symbols exercise the builtin registry (not a filesystem `.so`).
</Step>
</Steps>

### `pg_stat_statements` (preload required)

```text
# postgresql.conf (or -c on the server command line)
shared_preload_libraries = 'pg_stat_statements'
```

Restart the postmaster so `_PG_init` runs while `process_shared_preload_libraries_in_progress` is true (hooks + shmem). Then:

```sql
CREATE EXTENSION pg_stat_statements;
SELECT * FROM pg_stat_statements LIMIT 5;
```

Calling the view without preload yields the same class of error as Postgres: *pg_stat_statements must be loaded via "shared_preload_libraries"*. Catalog objects can still be created if you only run `CREATE EXTENSION` without preload; tracking will not be active.

### Logical decoding plugin (`test_decoding`)

`test_decoding` is not a `CREATE EXTENSION` module. It registers as a **builtin output plugin** (`register_builtin_output_plugin`, name `test_decoding`). Use it where Postgres expects a plugin library name on a logical decoding slot (for example `pg_create_logical_replication_slot(..., 'test_decoding')`). Dispatch goes through the builtin output-plugin path, not the library-symbol table.

## Ported modules inventory

### CREATE EXTENSION–ready (control + SQL in share, builtin library registered)

| Extension | Builtin library name | Default version (control) | Crate | Notes |
| --- | --- | --- | --- | --- |
| `citext` | `citext` | `1.8` | `crates/contrib/citext` | Case-insensitive text; type I/O reuses core `text`. |
| `hstore` | `hstore` | `1.8` | `crates/contrib/hstore` | Scalar + GiST/GIN ops ported; subscript handler not wired. |
| `ltree` | `ltree` | `1.3` | `crates/contrib/ltree` | Scalar path ops; btree/hash OK; GiST support symbols are loud stubs. |
| `pg_trgm` | `pg_trgm` | `1.6` | `crates/contrib/pg_trgm` | Similarity + most GIN/GiST strategies; regexp NFA strategies unported. |
| `pgcrypto` | `pgcrypto` | `1.4` | `crates/contrib/pgcrypto` | Digest/HMAC/crypt/PGP suite; GUC `pgcrypto.builtin_crypto_enabled`. |
| `pg_stat_statements` | `pg_stat_statements` | `1.12` | `crates/contrib/pg_stat_statements` | Requires `shared_preload_libraries`. |
| `uuid-ossp` | `uuid-ossp` | `1.1` | `crates/contrib/uuid_ossp` | Generators only; core `uuid` type is separate. |
| `injection_points` | `injection_points` | `1.0` | `crates/contrib/injection_points` | Test/harness extension; stats helpers raise not-supported. |
| `plpgsql` | `plpgsql` | `1.0` | `crates/pl/plpgsql/...` | Auto-created at initdb; not a contrib crate under `crates/contrib`. |

Control files set `module_pathname = '$libdir/<name>'` (and trust/relocatable flags). Example (`hstore.control`):

```text
comment = 'data type for storing sets of (key, value) pairs'
default_version = '1.8'
module_pathname = '$libdir/hstore'
relocatable = true
trusted = true
```

### Builtin library, no vendored extension control in clone share

| Module | Library / registry | Status |
| --- | --- | --- |
| `pg_prewarm` | `pg_prewarm` | `pg_prewarm()` ported and registered. `autoprewarm_*` symbols are loud stubs. No `pg_prewarm.control` / install SQL under `vendor/postgres-18.3/share/extension/` in this tree—`CREATE EXTENSION pg_prewarm` is not available from the vendored share set. |
| `test_decoding` | output-plugin name `test_decoding` | Logical decoding only (see above). |
| `regress` | `regress` | Regression-helper library for the test suite, not a user extension. |

### Partial / non-extension ports under `crates/contrib`

| Crate | What exists | What is missing for SQL use |
| --- | --- | --- |
| `earthdistance` | Great-circle distance math (`geo_distance_internal`) | No `register_builtin_library`, no control/SQL, not wired in `seams-init::init_all`. |
| `amcheck` (`verify_nbtree`, `verify_heapam`, …) | Verifier logic installed via seams for internal/SQL entry points as ported | No `amcheck.control` in share; not a full contrib extension package in this tree. |

### Known feature gaps inside otherwise-installable modules

| Module | Gap | Failure shape |
| --- | --- | --- |
| `hstore` | `hstore_subscript_handler` | Explicit “not yet wired” error. |
| `pg_trgm` | Regexp / case-insensitive regexp index strategies (`trgm_regexp` NFA) | Explicit “unported” error on those strategies; other strategies work. |
| `ltree` | `gist_ltree_ops` / `gist__ltree_ops` support functions | Symbols present for `CREATE EXTENSION` validation; using GiST indexes panics until generic GiST extproc keystone is complete. |
| `pg_prewarm` | `autoprewarm_start_worker`, `autoprewarm_dump_now` | Loud unported errors if called. |
| `injection_points` | Per-point / fixed statistics | “not ported” errors on stats SQL functions. |

## Share directory layout

Clone-oriented share (what `--initdb -L` / `PGRUST_PGSHAREDIR` should see for contrib install scripts):

:::files
vendor/postgres-18.3/share/
├── extension/
│   ├── citext.control + citext--*.sql
│   ├── hstore.control + hstore--1.8.sql
│   ├── ltree.control + ltree--*.sql
│   ├── pg_trgm.control + pg_trgm--*.sql
│   ├── pgcrypto.control + pgcrypto--*.sql
│   ├── pg_stat_statements.control + pg_stat_statements--*.sql
│   ├── uuid-ossp.control + uuid-ossp--*.sql
│   ├── injection_points.control + injection_points--1.0.sql
│   └── plpgsql.control + plpgsql--1.0.sql
├── postgres.bki
├── system_*.sql
└── timezone/ …
:::

Several crates also keep a copy of control/SQL under `crates/contrib/<mod>/extension/` (for example hstore, citext, pg_trgm). Runtime install uses the **share** tree the binary was built/bootstrapped with, not the crate path.

<Note>
Docker images may install a full PGDG PostgreSQL share tree for bootstrap templates and `psql`. That tree can list many more extension control files than pgrust has ported. Presence of a `.control` file does **not** mean the module is a pgrust builtin—only the inventory above is backed by `register_builtin_library` / output-plugin registration.
</Note>

## Runtime configuration that interacts with contrib

| Setting | Role |
| --- | --- |
| Share path (`PGRUST_PGSHAREDIR`, initdb `-L`) | Locates `extension/*.control` and scripts. |
| `shared_preload_libraries` | Required for `pg_stat_statements` hooks/shmem. |
| `LOAD 'name'` | Runs builtin `pg_init` once if registered (for example pgcrypto GUCs). |
| `pgcrypto.builtin_crypto_enabled` | `on` (default) / `off` / `fips`; `off` disables built-in crypto entry points. |
| `pg_trgm.similarity_threshold` and related | Registered from pg_trgm `pg_init` (defaults match Postgres: 0.3 / 0.6 / 0.5). |
| `io_method=sync`, stack GUCs | Server-wide pgrust requirements; not extension-specific but required for a stable session. |

## What remains unported

- **Generic Postgres extensions** (any `$libdir` module without a pgrust builtin registration), including PL/Python, PL/Perl, PL/Tcl and typical third-party C extensions—README status: not generally compatible.
- **Full upstream contrib set**: only the modules in the inventory tables are present under `crates/contrib` / plpgsql. Everything else is absent as a port (no registry entry).
- **In-module stubs** listed above (hstore subscripts, pg_trgm regexp NFA, ltree GiST, autoprewarm, injection_points stats).
- **Extension admin SRFs / some ALTER paths** still deferred in `backend-commands-extension` (for example `pg_available_extensions`, parts of `ALTER EXTENSION … ADD/DROP`). Day-to-day `CREATE EXTENSION` for the listed modules uses the ported control + script pipeline.

## Troubleshooting

| Symptom | Likely cause | What to check |
| --- | --- | --- |
| `could not open extension control file` / extension does not exist | Share path wrong or control not in that tree | `-L` / `PGRUST_PGSHAREDIR`; `ls $share/extension/*.control` |
| `could not find function "…" in file "$libdir/…"` | Symbol missing from builtin `lookup`, or library not registered | Confirm module is in the CREATE EXTENSION–ready table; rebuild so `seams-init` includes the crate |
| Dynamic loader / missing `.so` errors for a random extension | Module not ported; fall-through to OS loader | Only use the inventory above; third-party C extensions are out of scope |
| `pg_stat_statements must be loaded via "shared_preload_libraries"` | Preload not set or postmaster not restarted | Set GUC, full restart, then `CREATE EXTENSION` |
| Extension creates but GiST/ltree index or regexp trgm fails | Known port gap | Use sequential scan / non-regexp strategies / btree for ltree until those paths land |
| `hstore` subscript errors | Subscript handler gap | Avoid `hstore` subscripting syntax; use operators/functions |
| Crypto functions refuse to run | `pgcrypto.builtin_crypto_enabled = off` | Reset GUC to `on` |

## Related pages

<CardGroup>
  <Card title="Seams and builtin libraries" href="/seams-and-builtins">
    Registry model, `register_builtin_library`, and how dfmgr replaces dlopen.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    `PGRUST_PGSHAREDIR`, `-L`, and why a wrong share tree breaks control/SQL lookup.
  </Card>
  <Card title="Compatibility and status" href="/compatibility">
    Extension/PL boundaries, non-production status, and regression-oracle model.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Broader failure modes including unported extension loads and boot settings.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Runtime GUCs including stack, `io_method`, and path env vars.
  </Card>
</CardGroup>

---

## 13. Run regression tests

> Clone-only scripts/run-regression against vendored PG 18.3 SQL/expected files, psql client requirements, selective test names, and optional isolation/auth/recovery TAP harnesses.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/13-run-regression-tests.md
- Generated: 2026-07-10T05:32:14.045Z

### Source Files

- `scripts/run-regression`
- `scripts/run-pg-regress`
- `scripts/run-pg-regress-serial`
- `scripts/run-pg-isolation`
- `scripts/run-auth-tap`
- `scripts/run-recovery-tap`
- `crates/backend/test_regress/src/lib.rs`

---
title: "Run regression tests"
description: "Clone-only scripts/run-regression against vendored PG 18.3 SQL/expected files, psql client requirements, selective test names, and optional isolation/auth/recovery TAP harnesses."
---

`scripts/run-regression` is the clone-only main-suite runner: it boots `target/release/postgres` with pgrust `--initdb` against `vendor/postgres-18.3/share`, feeds vendored SQL from `vendor/postgres-18.3/regress/sql` through a PostgreSQL 18 `psql` client, and diffs each result against `vendor/postgres-18.3/regress/expected`. No PostgreSQL source tree (`PG_SRC`) and no C `initdb` install are required for that path. Optional scripts (`run-pg-regress-serial`, `run-pg-isolation`, `run-auth-tap`, `run-ldap-tap`, `run-recovery-tap`) still need an external PG 18.3 source and C install prefix.

## What the clone-only runner needs

| Input | Role |
|-------|------|
| `target/release/postgres` | Server and `--initdb` (override with `PGRUST_BIN`) |
| `vendor/postgres-18.3/share` | Bootstrap catalogs, tz data, samples (`-L` for initdb) |
| `vendor/postgres-18.3/regress/{sql,expected,data,parallel_schedule}` | Suite inputs and oracle |
| PostgreSQL 18 `psql` | Client only (only external dependency) |

<Note>
`scripts/run-pg-regress` is a thin wrapper that `exec`s `scripts/run-pg-regress-serial`. That serial harness is **not** clone-only: it requires `PG_SRC` and `PG_INSTALL` and uses C `initdb` / `psql` / `createdb`. Prefer `scripts/run-regression` for a fresh clone.
</Note>

## Prerequisites

1. Release binary:

```bash
cargo build --release --locked --bin postgres
```

2. Vendored tree present under `vendor/postgres-18.3/` (checked for `share/postgres.bki`, `regress/sql`, `regress/expected`, `regress/parallel_schedule`).

3. A PostgreSQL 18 `psql` client, resolved in order:

| Priority | Source |
|----------|--------|
| 1 | `PGRUST_PSQL` |
| 2 | `command -v psql` |
| 3 | `/tmp/pgrust_pginstall/bin/psql` |

If none is found, the script exits with install hints (`brew install libpq` on macOS, or set `PGRUST_PSQL`).

## Run the main suite

<Steps>
<Step title="Full parallel_schedule order">
```bash
PGRUST_BIN="$PWD/target/release/postgres" \
scripts/run-regression
```
With no arguments, the runner flattens every `test:` line from `vendor/postgres-18.3/regress/parallel_schedule` into a **serial** order (groups are not run in parallel).
</Step>
<Step title="Selective tests">
```bash
scripts/run-regression boolean int4 join
```
Named tests run only those SQL files. If `test_setup` is not in the argument list, it is auto-prepended so fixtures (`onek` / `tenk` / …) exist.
</Step>
<Step title="Stop early while debugging">
```bash
STOP_AFTER=join scripts/run-regression
```
Stops after the named test finishes.
</Step>
</Steps>

### Environment (clone-only runner)

| Variable | Default | Purpose |
|----------|---------|---------|
| `PGRUST_BIN` | `$repo/target/release/postgres` | pgrust server binary |
| `PGRUST_PSQL` | auto-detect | `psql` client path |
| `PGPORT` | `55434` | postmaster port |
| `OUTDIR` | temp under `$TMPDIR` / `/tmp` | logs, outputs, `_status.tsv` |
| `STOP_AFTER` | unset | halt after this test name |
| `RUST_MIN_STACK` | `33554432` | Rust stack floor |
| `PGTZ` | `America/Los_Angeles` | match oracle timezone |
| `PGDATESTYLE` | `Postgres, MDY` | match oracle date style |
| `PGOPTIONS` | `-c intervalstyle=postgres_verbose` | match interval output |

Client locale is forced toward C (`LANG` / `LC_MESSAGES`, with several `LC_*` unset) so diffs stay stable.

### Server process settings applied by the runner

The postmaster is started with (among others):

- `-c listen_addresses=` (Unix socket only)
- `-k` short socket dir under `/tmp/pgr.XXXXXX` (macOS `sun_path` limit)
- `-c io_method=sync` (required; default worker IO is unported)
- `-c max_stack_depth=60000`
- `-c checkpoint_timeout=3600 -c autovacuum=on -c max_logical_replication_workers=0`
- `ulimit -s 65520` when the shell allows it

Init uses:

```bash
"$PGRUST_BIN" --initdb -D "$DATADIR" -L "$SHARE" \
  --no-locale --encoding UTF8 -U postgres
```

## Execution model

```text
run-regression
  ├─ validate vendor + PGRUST_BIN + psql
  ├─ --initdb -L vendor/.../share
  ├─ postmaster (io_method=sync, short socket)
  ├─ CREATE DATABASE regression
  ├─ for each test in schedule / args:
  │    psql < sql/T.sql  > OUTDIR/T.out
  │    best-of expected/T.out | T_1.out | T_2.out
  │    status: PASS | FAIL | PSQL_FAIL | CRASHED
  └─ summary PASS=n/total … ; exit 1 if any non-PASS
```

| Status | Meaning |
|--------|---------|
| `PASS` | exact match vs best expected file (0-line `diff`) |
| `FAIL` | output differs; first ~40 diff lines printed |
| `PSQL_FAIL` | `psql` non-zero exit |
| `CRASHED` | postmaster no longer answers `SELECT 1` after the test |

psql is invoked with `-X -a -q` and variables:

- `HIDE_TABLEAM=on`, `HIDE_TOAST_COMPRESSION=on`
- `abs_srcdir` → vendored `regress/`
- `abs_builddir` → `OUTDIR` (and `OUTDIR/results` is pre-created for COPY / largeobject)
- `libdir='$libdir'`, `dlsuffix='.so'` (path need not exist on disk; see below)

### Vendored layout

:::files
vendor/postgres-18.3/
  share/                 # install-time share/postgresql (postgres.bki, timezone, …)
  regress/
    sql/                 # ~230 SQL scripts
    expected/            # oracle .out (+ optional _1 / _2 variants)
    data/                # COPY fixtures (onek.data, tenk.data, …)
    parallel_schedule    # test: groups (flattened serially by the runner)
  isolation/             # specs + expected + isolation_schedule (not clone-only)
:::

`vendor/postgres-18.3/README.md` states isolation files are for a separate `pg_isolation_regress` runner and are **not** covered by the clone-only script.

## Regress helpers as a builtin library

`test_setup.sql` creates C-language functions via `AS '$libdir/regress', '<symbol>'`. pgrust cannot `dlopen` a real `regress.so`. Instead `crates/backend/test_regress` ports the helpers and registers library name `regress` with `dfmgr_seams::register_builtin_library` from `test_regress::init_seams()`, which `seams-init` calls at process startup.

`scripts/run-regression` still defines `:libdir` / `:dlsuffix` so the constructed path is well-formed; the dynamic loader strips directory and suffix and resolves basename `regress` in-process.

Ported symbols include (non-exhaustive): `binary_coercible`, `regress_setenv`, `test_atomic_ops`, encoding helpers used by `conversion.sql` / `encoding.sql`, `widget_in` / `widget_out`, `get_environ`, and others returned by the crate’s `lookup` table. Unknown symbols resolve as if missing from `regress.so`.

## Optional harnesses (need C PG 18.3)

These are for deeper suites. They share patterns: pgrust as the **server**, C tools for client/bootstrap, and `io_method=sync` injected somewhere.

| Script | Requires | Default port / notes |
|--------|----------|----------------------|
| `scripts/run-pg-regress` | `PG_SRC`, `PG_INSTALL` | wraps serial harness |
| `scripts/run-pg-regress-serial` | `PG_SRC`, `PG_INSTALL` | default `PGPORT=55433`; C initdb + real `$PG_LIBDIR` for `libdir` |
| `scripts/run-pg-isolation` | `PG_SRC`, `PG_INSTALL`, built `pg_isolation_regress` | default `PGPORT=55434`; `--use-existing` against a running pgrust; optional `PG_BUILD` / `PG_ISOLATION_REGRESS` |
| `scripts/run-auth-tap` | `PG_SRC`, `PG_INSTALL`, `prove`, IPC::Run | fake install; default tests `t/003_peer.pl` … `t/007_pre_auth.pl` |
| `scripts/run-ldap-tap` | same pattern | LDAP TAP under `src/test/ldap` |
| `scripts/run-recovery-tap` | same + more C tools | default archive-recovery group; `PG_TEST_TIMEOUT_DEFAULT` default `90` |

### TAP “fake install” contract

Auth / LDAP / recovery TAP scripts:

1. Build `$INST/bin` with symlinks to `$PG_INSTALL/bin/*`.
2. **Copy** C `pg_ctl` and **copy** pgrust as `postgres` (not symlinks) so `find_other_exec` keeps realpath inside `$INST/bin` and launches pgrust, not C postgres.
3. Self-check: `strings` on resolved postgres must contain `PGRUST_TRACE`.
4. Inject via `TEMP_CONFIG`:

```text
io_method = sync
max_stack_depth = 7000kB
```

5. Export capability flags so Perl `skip_all` does not treat unset env as fatal (`with_icu=no`, `with_ssl=openssl_no`, …). Auth also sets `enable_injection_points=no`.
6. Point `PG_REGRESS` at the C tree’s `pg_regress` binary for Cluster.pm `--config-auth`.

Cluster **bootstrap** intentionally still uses C `initdb` (which runs C `postgres --boot` via realpath escape); only the long-running server is pgrust.

### Isolation example

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/path/to/pg-install
# optional: PG_ISOLATION_REGRESS=/path/to/pg_isolation_regress
scripts/run-pg-isolation
# or: scripts/run-pg-isolation some_spec_name
```

### Recovery TAP example

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/tmp/pgrust_pginstall
export PGRUST_BIN="$PWD/target/release/postgres"
scripts/run-recovery-tap
# or: scripts/run-recovery-tap t/002_archiving.pl
```

## Expected outputs and success signals

Clone-only runner stderr includes:

```text
regression output: <OUTDIR>
psql client:       <path>
socket dir:        /tmp/pgr.XXXXXX
running pgrust --initdb ...
```

Per-test TSV lines (also in `$OUTDIR/_status.tsv`):

```text
test_name<TAB>STATUS<TAB>diff_lines<TAB>best_expected_path
```

Final summary:

```text
PASS=n/total  FAIL=…  PSQL_FAIL=…  CRASHED=…
```

Exit code `0` only when every status is `PASS`. README documents a verified launch result matching Postgres expected output across 46k+ regression queries; treat that as a project milestone signal, not a guarantee of your local host.

## Failure modes

| Symptom | Likely cause |
|---------|----------------|
| `pgrust postgres not found` | Build release binary or set `PGRUST_BIN` |
| `no psql client found` | Install PG 18 client or set `PGRUST_PSQL` |
| `missing vendored file/dir` | Incomplete checkout of `vendor/postgres-18.3` |
| `pgrust --initdb failed` | Inspect `$OUTDIR/initdb.log` (share path / flags) |
| `postmaster did not start` / died | Inspect `$OUTDIR/postmaster.log`; often missing `io_method=sync` or stack limits if you reimplemented the boot line |
| Many `FAIL` with locale/tz noise | Do not override `PGTZ` / date style / intervalstyle away from runner defaults |
| `could not access file` for regress helpers | Builtin `regress` not registered (build without seams-init path) or bad `:libdir` expansion |
| TAP times out / measures C postgres | Fake-install regressed (symlink-only `pg_ctl`/`postgres`); self-check should fail without `PGRUST_TRACE` |
| TAP false FAIL on skip tests | Unset `with_*` / `enable_injection_points` under `warnings FATAL` |

## Related pages

<CardGroup>
<Card title="Build from source" href="/build-from-source">
Produce `target/release/postgres` and bake share paths.
</Card>
<Card title="Test harness environment reference" href="/test-harness-reference">
Full env defaults for run-regression, serial, isolation, and TAP scripts.
</Card>
<Card title="Compatibility and status" href="/compatibility">
Regression-oracle model and non-production boundaries.
</Card>
<Card title="Seams and builtin libraries" href="/seams-and-builtins">
How `regress` and contrib replace `dlopen`.
</Card>
<Card title="Run the server" href="/run-server">
Required `io_method=sync` and stack settings outside the harness.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Missing psql, stack overflows, share path, and boot failures.
</Card>
</CardGroup>

---

## 14. postgres CLI reference

> Flags from --help, must-be-first dispatch options, -c/--NAME=VALUE GUC overrides, and exit paths for --version and --describe-config.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/14-postgres-cli-reference.md
- Generated: 2026-07-10T05:31:26.711Z

### Source Files

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

---
title: "postgres CLI reference"
description: "Flags from --help, must-be-first dispatch options, -c/--NAME=VALUE GUC overrides, and exit paths for --version and --describe-config."
---

The single `postgres` binary is the C-ABI entry shell over `pg_main`. After seam registration and `TopMemoryContext` setup, it owns argv, dispatches by the first argument, and maps print-only paths (`--help`, `--version`, `--describe-config`) to stdout + `exit(0)`. Postmaster and backend switches share `process_postgres_switches`, which applies short flags and `-c` / `--NAME=VALUE` as GUC overrides under `PGC_POSTMASTER` / `PGC_S_ARGV`.

## Binary entry and outcomes

| Stage | Behavior |
| --- | --- |
| Argv | Native: `std::env::args()`. Wasm: host-provided argv (fallback `["postgres"]` if empty). |
| Init | Leak `TopMemoryContext`, call `init::init_all()`, then `pg_main(mcx, argv)`. |
| Success print | `MainOutcome::PrintAndExit(text)` → write stdout, `exit(0)`. |
| Initdb | `MainOutcome::Initdb` → `initdb_main`; `exit(0)` or `exit(1)`. Not built on wasm (`exit(1)`). |
| Dispatch | `MainOutcome::Dispatched` if a subprogram returned (logic error); shell still `exit(0)`. |
| Fatal startup | `Err` → `progname: {err}` on stderr, `exit(1)`. |

```text
argv[1] early scan
  --help | -?        → help text, exit 0
  --version | -V     → version banner, exit 0
  initdb | --initdb  → initdb driver (pgrust; not a DispatchOption)
  --describe-config  → skip root check
  -C <name> (first)  → skip root check
root check (unless skipped)
dispatch from "--*" first arg
  --check / --boot / --describe-config / --single / (default postmaster)
```

## Print-and-exit paths

These must appear as **argv[1]** (exact match). Later arguments are not scanned for them.

| Argument | Output | Exit |
| --- | --- | --- |
| `--help` or `-?` | Full help string from `help(progname)` | `0` |
| `--version` or `-V` | `postgres (PostgreSQL) 18.3\n` (`PG_BACKEND_VERSIONSTR`) | `0` |
| `--describe-config` | Tab-separated GUC dump from `GucInfoMain` | `0` |

### `--help` content (flags advertised)

**Options**

| Flag | Meaning |
| --- | --- |
| `-B NBUFFERS` | shared buffers |
| `-c NAME=VALUE` | set run-time parameter |
| `-C NAME` | print value of run-time parameter, then exit (advertised; see [Root check and `-C`](#root-check-and--c)) |
| `-d 1-5` | debugging level |
| `-D DATADIR` | database directory |
| `-e` | European date input (DMY) |
| `-F` | turn fsync off |
| `-h HOSTNAME` | listen host |
| `-i` | enable TCP/IP (deprecated) |
| `-k DIRECTORY` | Unix-domain socket location |
| `-l` | enable SSL |
| `-N MAX-CONNECT` | max connections |
| `-p PORT` | listen port |
| `-s` | show statistics after each query |
| `-S WORK-MEM` | work_mem (kB) |
| `-V, --version` | version, then exit |
| `--NAME=VALUE` | set run-time parameter |
| `--describe-config` | describe configuration parameters, then exit |
| `-?, --help` | help, then exit |

**Developer options:** `-f s|i|o|b|t|n|m|h`, `-O`, `-P`, `-t pa|pl|ex`, `-T`, `-W NUM`.

**Single-user mode:** `--single` (must be first), `DBNAME`, `-d 0-5`, `-E`, `-j`, `-r FILENAME`.

**Bootstrapping mode:** `--boot` / `--check` (must be first), `DBNAME` (mandatory for bootstrap), `-r FILENAME`.

Bug report address in help: `pgsql-bugs@lists.postgresql.org`. Home page line: `https://www.postgresql.org/`.

### `--describe-config` row format

Visible built-in GUCs only. Hidden when flags include `GUC_NO_SHOW_ALL`, `GUC_NOT_IN_SAMPLE`, or `GUC_DISALLOW_IN_FILE`. Rows sorted by name. One line per GUC:

```text
name \t context \t group \t TYPE \t reset \t min \t max \t short_desc \t long_desc
```

| Column | Notes |
| --- | --- |
| `TYPE` | `BOOLEAN`, `INTEGER`, `REAL`, `STRING`, `ENUM` |
| `reset` for BOOL/INT/REAL | Zero-initialized C `reset_val` parity: `FALSE` / `0` / `0` (not boot defaults) |
| `min` / `max` | From static metadata for INT/REAL; empty for BOOL/STRING/ENUM |
| STRING/ENUM “reset” | STRING uses `boot_val` (or empty); ENUM uses enum name for `boot_val` |

Root may run `--describe-config` (read-only path).

## Must-be-first dispatch options

After the early scan and optional root check, if `argv[1]` starts with `--`, the name after the prefix is passed to `parse_dispatch_option`. Unknown names fall through to postmaster.

| First argument | `DispatchOption` | Target |
| --- | --- | --- |
| *(none / not `--*` / unmatched)* | `DISPATCH_POSTMASTER` | `PostmasterMain` |
| `--check` | `DISPATCH_CHECK` | `BootstrapModeMain(..., check_only=true)` |
| `--boot` | `DISPATCH_BOOT` | `BootstrapModeMain(..., check_only=false)` |
| `--describe-config` | `DISPATCH_DESCRIBE_CONFIG` | `GucInfoMain` → print and exit |
| `--single` | `DISPATCH_SINGLE` | single-user backend (`get_user_name_or_exit` + `postgres_single_user_main`) |
| `--forkchild…` | never matched | Non-`EXEC_BACKEND` builds skip `forkchild`; treated as postmaster |

<Warning>
Dispatch long options must be **argv[1]**. If they appear later (for example after `-D`), switch parsers reject them with `--{name} must be first argument` (`ERRCODE_SYNTAX_ERROR`).
</Warning>

### pgrust-only early entry (not a `DispatchOption`)

| First argument | Behavior |
| --- | --- |
| `initdb` or `--initdb` | `MainOutcome::Initdb` → in-process initdb driver; re-execs this binary for `--boot` / `--single` phases |

Full initdb argv is documented separately; this page only records the dispatch hook.

## Root check and `-C`

Unless skipped, `check_root` fails when:

- effective uid is `0` → FATAL: `"root" execution of the PostgreSQL server is not permitted...`
- real uid ≠ effective uid → FATAL: `{progname}: real and effective user IDs must match`

**Root check skipped when argv[1] is:**

- `--describe-config`, or
- `-C` with a following argument (`argv.len() > 2`)

`-C` is listed in `--help` as “print value of run-time parameter, then exit”. In `process_postgres_switches`, short option `C` is currently a no-op (“ignored for consistency with the postmaster”). The early root-check bypass remains for pg_ctl-style privileged probes; do not assume a stock Postgres-style print-and-exit until that path is fully wired in postmaster option handling.

## GUC overrides: `-c` and `--NAME=VALUE`

Postmaster and backend share `process_postgres_switches` (getopt optstring  
`B:bC:c:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:v:W:-:`).

When context is `PGC_POSTMASTER` (secure):

- Source is `PGC_S_ARGV`
- A leading `--single` at `argv[1]` is dropped before getopt (postmaster path does not use this; single-user does)

### Long-option parsing (`ParseLongOption`)

| Input form | Result |
| --- | --- |
| `name=value` | name + value; `-` in name becomes `_` |
| `name` only | name only; value missing → error for `-c` / `--` GUC form |

Examples (equivalent):

```bash
postgres -D /data -c listen_addresses= -c io_method=sync
postgres -D /data --listen_addresses= --io_method=sync
postgres -D /data -c max_stack_depth=60000
```

Missing value errors:

- `--{optarg} requires a value`
- `-c {optarg} requires a value`

### Short flags → GUCs (secure / postmaster-relevant)

| Flag | GUC / effect |
| --- | --- |
| `-B` | `shared_buffers` |
| `-b` | binary upgrade (`IsBinaryUpgrade=true`) when secure |
| `-c` / `--` | `set_config_option(name, value, …)` |
| `-D` | data directory (`userDoption` / postmaster re-scan of argv) |
| `-d` | `set_debug_options`: `log_min_messages=debugN` or `notice`; levels ≥1 also set `log_connections`/`log_disconnections` under postmaster; ≥2 `log_statement=all`; ≥3–5 parse/plan/rewrite debug prints |
| `-e` | `datestyle=euro` |
| `-E` | echo queries (secure) |
| `-F` | `fsync=false` |
| `-f X` | disable plan type (`s/i/o/b/t/n/m/h` → `enable_seqscan` … `enable_hashjoin` = false) |
| `-h` | `listen_addresses` |
| `-i` | `listen_addresses=*` |
| `-j` | semi/newline delimiter mode (secure) |
| `-k` | `unix_socket_directories` |
| `-l` | `ssl=true` |
| `-N` | `max_connections` |
| `-O` | `allow_system_table_mods=true` |
| `-P` | `ignore_system_indexes=true` |
| `-p` | `port` |
| `-r` | redirect stdout/stderr path (secure) |
| `-S` | `work_mem` |
| `-s` | `log_statement_stats=true` |
| `-t pa\|pl\|ex` | `log_parser_stats` / `log_planner_stats` / `log_executor_stats` |
| `-v` | standalone `FrontendProtocol` override (secure) |
| `-W` | `post_auth_delay` |
| `-n`, `-T`, `-C` | ignored in this switch table (postmaster consistency) |

### pgrust extension flag

| Flag | Meaning |
| --- | --- |
| `--regress-output` | Valueless; enables psql `-a -q`-compatible single-user output for regression diffs. Not a GUC. |

### Invalid argument FATAL

If getopt fails or leftover non-option tokens remain (except the optional database name for standalone):

- Under postmaster: `invalid command-line argument for server process: {bad}`
- Standalone: `{progname}: invalid command-line argument: {bad}`
- Hint: `Try "{progname}" --help" for more information.`

## Postmaster argv after dispatch

Default path calls `PostmasterMain`, which:

1. `InitializeGUCOptions`
2. `process_postgres_switches(argv, PGC_POSTMASTER)`
3. Re-reads `-D` / `-Ddir` from argv for `SelectConfigFiles`
4. Continues control-file checks, listen sockets from `listen_addresses` / `unix_socket_directories` / `port` / `max_connections` (via postmaster GUC readers)

Example (from project README shape):

```bash
RUST_MIN_STACK=33554432 target/release/postgres \
  -D /tmp/pgrust-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000
```

Required runtime GUCs and stack limits are covered on the configuration and run-server pages; the CLI only delivers them as `-c` / `--` overrides or config files under `-D`.

## Bootstrap / check switch surface

`--boot` / `--check` use a smaller getopt set (`B:c:d:D:Fkr:X:-:` plus long options). Same rules: misplaced dispatch long options error with `--{name} must be first argument`; `-c` / `--NAME=VALUE` require values.

## Exit code summary

| Path | Code |
| --- | --- |
| `--help`, `--version`, `--describe-config` | `0` |
| initdb success | `0` |
| initdb error / wasm initdb | `1` |
| Startup `PgError` from `pg_main` | `1` |
| `SelectConfigFiles` failure in postmaster | `ExitPostmaster(2)` |
| Other postmaster shutdowns | `ExitPostmaster(0|1)` per state machine |

## Verification signals

```bash
# Help and version (no data dir)
./target/release/postgres --help | head -5
# expected: "... is the PostgreSQL server."

./target/release/postgres -V
# expected: postgres (PostgreSQL) 18.3

./target/release/postgres --describe-config | head -3
# expected: tab-separated GUC rows (name, context, group, TYPE, ...)

# Misplaced must-be-first option (after other args) → syntax error when switches run
# Root as euid 0 without --describe-config/-C → FATAL root refusal on stderr, exit 1
```

## Related pages

<CardGroup>
  <Card title="Process modes and dispatch" href="/process-modes">
    How postmaster, --single, --boot, --check, --describe-config, and initdb share one binary.
  </Card>
  <Card title="initdb options reference" href="/initdb-reference">
    Supported `postgres --initdb` argv, space-separated parsing, and unsupported flags.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `io_method`, stack settings, listen_addresses, and build-time `PGRUST_*` path vars.
  </Card>
  <Card title="Run the server" href="/run-server">
    Start postmaster with `-D`, sockets, required overrides, and readiness checks.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Root execution refused, wrong share path, stack overflows, and initdb option gaps.
  </Card>
</CardGroup>

---

## 15. initdb options reference

> Supported pgrust initdb argv: -D/--pgdata, -U/--username, -L, -E/--encoding, locale flags, --wal-segsize; space-separated parsing; unsupported options that error.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/15-initdb-options-reference.md
- Generated: 2026-07-10T05:32:04.882Z

### Source Files

- `crates/backend/main/initdb/src/lib.rs`
- `docker/entrypoint.sh`
- `README.md`
- `crates/backend/main/main_main/src/lib.rs`
- `crates/_support/seam/init/src/bin/postgres.rs`

---
title: "initdb options reference"
description: "Supported pgrust initdb argv: -D/--pgdata, -U/--username, -L, -E/--encoding, locale flags, --wal-segsize; space-separated parsing; unsupported options that error."
---

pgrust clusters the initdb surface inside the single `postgres` binary: when `argv[1]` is `initdb` or `--initdb`, `pg_main` returns `MainOutcome::Initdb` and the binary shell runs `initdb::initdb_main`. That driver is a port of C `src/bin/initdb/initdb.c` options and scaffolding; it is **not** a separate `initdb` executable and is **not** a PostgreSQL must-be-first `DispatchOption`. Argument parsing lives in `parse_args` and accepts only a small, explicit subset of C initdb flags. Anything else fails with `unrecognized initdb option "…"`.

<Warning>
pgrust initdb is a **subset** of C PostgreSQL 18 initdb. Options common in Docker/`POSTGRES_INITDB_ARGS` recipes (`-A`/`--auth`, `--data-checksums`, `--pwfile`, `--waldir`, and many others) are **not** accepted and abort the run.
</Warning>

## Invocation

| Form | Notes |
|------|--------|
| `postgres --initdb …` | Canonical form used in README and Docker |
| `postgres initdb …` | Same dispatch (`argv[1] == "initdb"`) |
| `pgrust initdb …` | Same when the binary is named `pgrust` |

`argv[0]` is the program name; `argv[1]` must be `initdb` or `--initdb`; options start at `argv[2]`.

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

On success the driver prints:

```text
Success. You can now start the database server using pgrust/postgres -D <pgdata>
```

and the process exits `0`. Failures print `initdb: error: <message>` on stderr and exit `1`.

<Note>
Initdb is not built for wasm targets (`target_family = "wasm"` exits `1` without running the driver). Native builds re-exec the same binary for internal `--boot` and `--single` phases.
</Note>

## Argument parsing rules

| Rule | Behavior |
|------|----------|
| Space-separated values | Required: `--pgdata /path`, `-E UTF8`, `--wal-segsize 16` |
| `=` forms | **Not** accepted — e.g. `--pgdata=/path` is treated as an unknown option string |
| Glued short form | **Only** `-D<path>` is accepted (e.g. `-D/tmp/data`). Other shorts need a following token (`-U postgres`) |
| Order | Options may appear in any order after `initdb`/`--initdb` |
| Missing value | `option <flag> requires an argument` |
| Unknown token | `unrecognized initdb option "<token>"` |

Docker documents the same contract: `pgrust initdb parses --opt value (space-separated), not --opt=value`.

## Supported options

### Data directory and identity

<ParamField body="-D / --pgdata" type="path" required>
Target `PGDATA` path. Required. Missing value yields `no data directory specified (use -D / --pgdata)`. Directory must not exist, or must exist and be empty; otherwise `directory "<path>" exists but is not empty`. Created with mode `0700` on Unix.
</ParamField>

<ParamField body="-U / --username" type="string" default="$USER or postgres">
Bootstrap superuser name. Default is `$USER` when set and non-empty, else `postgres`. Substituted into the BKI as the `POSTGRES` token (superuser role name). Does not consult `/etc/passwd` for the name (Docker relies on `--username` only).
</ParamField>

### Share directory

<ParamField body="-L" type="path">
Share-directory override (bootstrap templates: `postgres.bki`, `*.conf.sample`, system SQL, etc.). If omitted, derived via `common_path_seams::get_share_path` from the executable path. Docker always passes `-L "$PG_SHAREDIR"` because the binary may not sit next to a PGDG-style share tree.
</ParamField>

### Encoding

<ParamField body="-E / --encoding" type="string" default="UTF8">
Server encoding name. Normalized by uppercasing and stripping `-`/`_`, then mapped to a numeric encoding id for BKI substitution.
</ParamField>

| Accepted name (after normalize) | Encoding id |
|---------------------------------|-------------|
| `SQLASCII` | `0` |
| `UTF8`, `UNICODE` | `6` (default) |
| `LATIN1` | `8` |

Any other name fails: `unsupported encoding "<name>"`.

### Locale

| Flag | Behavior |
|------|----------|
| Default | `locale_collate = "C"`, `locale_ctype = "C"` |
| `--lc-collate <value>` | Stores value; substituted into BKI as `LC_COLLATE` |
| `--lc-ctype <value>` | Stores value; substituted into BKI as `LC_CTYPE` |
| `--locale <value>` | Value is **accepted and consumed**, but not applied to collate/ctype — only `"C"` is effectively honored for cluster locale messaging |
| `--no-locale` | Forces both collate and ctype to `"C"` |

Startup still prints `The database cluster will be initialized with locale "C".` regardless of stored collate/ctype strings. BKI also sets `DATLOCALE`/`ICU_RULES` to `_null_` and `LOCALE_PROVIDER` to `c` (libc).

### WAL segment size

<ParamField body="--wal-segsize" type="integer (MB)" default="16">
WAL segment size in **megabytes**. Parsed as `u64`; stored as `mb * 1024 * 1024` bytes and passed to the bootstrap backend as `-X <bytes>`. Non-integer values fail: `invalid --wal-segsize`.
</ParamField>

## Defaults summary

| Field | Default |
|-------|---------|
| `pgdata` | empty (error if still empty after parse) |
| `username` | `$USER` or `postgres` |
| `sharedir` | derived from executable unless `-L` |
| `encoding_id` | `6` (UTF8) |
| `wal_segment_size` | `16 * 1024 * 1024` bytes |
| `locale_collate` / `locale_ctype` | `"C"` / `"C"` |
| Auth in generated `pg_hba.conf` | `trust` (host and local); no `-A` support |

## Unsupported options (error)

Any argv token not listed above is rejected:

```text
initdb: error: unrecognized initdb option "<token>"
```

Documented or commonly attempted gaps include:

| Option / area | Status in pgrust |
|---------------|------------------|
| `-A` / `--auth` / `--auth-host` / `--auth-local` | Unrecognized |
| `--data-checksums` | Unrecognized |
| `--pwfile` / `-W` / `--pwprompt` | Unrecognized (Docker sets password later via `ALTER ROLE`) |
| `--waldir` / `POSTGRES_INITDB_WALDIR` path | Unrecognized if passed through to `--initdb` |
| `--locale=…` / `--encoding=…` (`=` form) | Unrecognized whole token |
| Other C initdb flags (`-k`, `--set`, `--sync-only`, …) | Unrecognized |

Docker still wires `POSTGRES_INITDB_ARGS` into the same driver. Only space-separated supported flags are safe there; unsupported args fail first boot.

## Error catalog

| Condition | Message (approx.) | Exit |
|-----------|-------------------|------|
| No `-D`/`--pgdata` | `no data directory specified (use -D / --pgdata)` | 1 |
| Option without value | `option <flag> requires an argument` | 1 |
| Unknown flag | `unrecognized initdb option "<a>"` | 1 |
| Bad encoding name | `unsupported encoding "<name>"` | 1 |
| Bad `--wal-segsize` | `invalid --wal-segsize` | 1 |
| Non-empty PGDATA | `directory "<pgdata>" exists but is not empty` | 1 |
| Cannot create/read paths | `could not create/read/write "…"` | 1 |
| BKI major mismatch | `input file "…/postgres.bki" does not belong to PostgreSQL 18` | 1 |
| Bootstrap/post-bootstrap backend failure | `bootstrap backend exited with …` / `post-bootstrap backend exited with …` | 1 |

All messages are prefixed with `initdb: error: ` by the binary shell.

## How options flow into bootstrap

```text
argv: postgres --initdb [options...]
              │
              ▼
        parse_args  ──► Options { pgdata, username, sharedir, encoding_id,
                                  wal_segment_size, locale_* }
              │
              ├─ create PGDATA tree + PG_VERSION ("18")
              ├─ setup_config from sharedir samples (hba = trust)
              ├─ substitute_bki (POSTGRES, ENCODING, LC_*)
              ├─ re-exec: postgres --boot -X <wal_bytes> -D <pgdata>  ← BKI stdin
              └─ re-exec: postgres --single … template1               ← post-bootstrap SQL
```

Relevant option effects:

- **username** → BKI `POSTGRES` token; privilege SQL uses the same name  
- **encoding_id** → BKI `ENCODING`  
- **locale_*** → BKI `LC_COLLATE` / `LC_CTYPE`  
- **wal_segment_size** → `--boot -X`  
- **-L / sharedir** → location of `postgres.bki`, conf samples, and post-bootstrap SQL files  

Auth-method flags are not parsed; generated `pg_hba.conf` always uses `trust` with the standard caution comment.

## Docker mapping

`docker_init_database_dir` runs approximately:

```bash
"$PGRUST_BIN" --initdb \
  -D "$PGDATA" \
  -L "$PG_SHAREDIR" \
  --username "$POSTGRES_USER" \
  $POSTGRES_INITDB_ARGS \
  "$@"
```

| Docker input | Effect |
|--------------|--------|
| `POSTGRES_USER` | Always passed as `--username` |
| `POSTGRES_INITDB_ARGS` | Appended as extra initdb argv (must be space-separated supported flags only) |
| `POSTGRES_PASSWORD` | **Not** an initdb flag; applied after temp server start via `ALTER ROLE` (no `--pwfile`) |
| Share path | Always `-L` (binary not in a classic bindir layout) |

See the Docker environment reference for full env var behavior.

## Minimal and extended examples

<CodeGroup>

```bash title="Minimal (required -D)"
postgres --initdb -D /tmp/pgrust-data -L /path/to/share
```

```bash title="README-shaped cluster"
postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

```bash title="Encoding + WAL size"
postgres --initdb \
  -D /tmp/pgrust-data \
  -L /path/to/share \
  -E LATIN1 \
  --wal-segsize 32 \
  -U app
```

```bash title="Invalid (will error)"
# equals form
postgres --initdb --pgdata=/tmp/pgrust-data

# C-only flags
postgres --initdb -D /tmp/pgrust-data -A trust --data-checksums
```

</CodeGroup>

## Related pages

<CardGroup>
  <Card title="Initialize a cluster" href="/init-cluster">
    End-to-end --initdb flow: directory layout, bootstrap phases, success text.
  </Card>
  <Card title="Process modes and dispatch" href="/process-modes">
    How argv[1] routes to initdb, --boot, --single, and postmaster.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    -L, PGRUST_PGSHAREDIR, and why a wrong share tree breaks initdb.
  </Card>
  <Card title="Docker environment reference" href="/docker-reference">
    POSTGRES_INITDB_ARGS, POSTGRES_USER, and entrypoint password handling.
  </Card>
  <Card title="postgres CLI reference" href="/cli-reference">
    Non-initdb flags: --help, --version, -c GUC overrides, dispatch options.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Initdb option gaps and other common boot failures.
  </Card>
</CardGroup>

---

## 16. Configuration reference

> Required and high-impact runtime settings: io_method default vs sync, max_stack_depth, RUST_MIN_STACK, ulimit -s, listen_addresses, and build-time PGRUST_* path env vars.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/16-configuration-reference.md
- Generated: 2026-07-10T05:31:36.706Z

### Source Files

- `crates/backend/utils/misc/guc_tables/src/tables.rs`
- `crates/backend/utils/misc/guc_tables/src/consts.rs`
- `crates/backend/storage/buffer/bufmgr/src/buf_aio.rs`
- `crates/backend/utils/init/miscinit/src/boot_paths.rs`
- `.cargo/config.toml`
- `docker/entrypoint.sh`
- `scripts/run-recovery-tap`

---
title: "Configuration reference"
description: "Required and high-impact runtime settings: io_method default vs sync, max_stack_depth, RUST_MIN_STACK, ulimit -s, listen_addresses, and build-time PGRUST_* path env vars."
---

pgrust boots a Postgres 18.3-shaped GUC table plus a small set of process and build-time knobs that the stock binary defaults do not safely satisfy. High-impact settings are applied as postmaster command-line `-c` / `--NAME=VALUE` overrides, `postgresql.conf` / `TEMP_CONFIG` lines, process environment (`RUST_MIN_STACK`), OS stack rlimit (`ulimit -s`), and compile-time `PGRUST_*` path env vars baked via `option_env!`.

## Required launch profile

Every supported local, Docker, and regression path pins the same core set:

| Layer | Setting | Value used in-tree | When applied |
| --- | --- | --- | --- |
| GUC | `io_method` | `sync` | Postmaster start (`PGC_POSTMASTER`) |
| GUC | `max_stack_depth` | `60000` (or `7000kB` in some TAP harnesses) | Superuser-settable (`PGC_SUSET`); harnesses set at start |
| Env | `RUST_MIN_STACK` | `33554432` (32 MiB) | Process environment before `postgres` exec |
| OS | stack rlimit | `ulimit -s 65520` | Shell / entrypoint before start |
| GUC | `listen_addresses` | empty (socket-only) or `*` (Docker public) | Postmaster start |

<RequestExample>
```bash
ulimit -s 65520

RUST_MIN_STACK=33554432 target/release/postgres \
  -D /tmp/pgrust-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000
```
</RequestExample>

Docker injects the same GUCs automatically via `PGRUST_SERVER_OPTS` and always appends `listen_addresses='*'` for the public server (temp init server uses `listen_addresses=''`).

```text
┌─────────────────────────────────────────────────────────────┐
│  Host process                                                │
│   ulimit -s 65520                                            │
│   RUST_MIN_STACK=33554432                                    │
│   postgres -D … -c io_method=sync -c max_stack_depth=60000  │
│              -c listen_addresses=…  (-h / -i / -k / -p)     │
└─────────────────────────────────────────────────────────────┘
         │ compile-time bake
         ▼
   PGRUST_PGSHAREDIR → share/timezone, timezonesets, templates
   (initdb also needs -L when binary layout ≠ share layout)
```

## `io_method`

| Field | Value |
| --- | --- |
| Name | `io_method` |
| Type | enum |
| Context | `PGC_POSTMASTER` (start-time only) |
| Group | resources / I/O |
| Boot default | `worker` (`IOMETHOD_WORKER = 1`) |
| Enum options | `sync` (`0`), `worker` (`1`) |
| Assign hook | `assign_io_method` |

Boot default matches Postgres (`DEFAULT_IO_METHOD = IOMETHOD_WORKER`). Operational launches **must** set `-c io_method=sync`:

- README, Docker entrypoint (`PGRUST_SERVER_OPTS`), `scripts/run-regression`, serial/isolation harnesses, and recovery TAP all force `sync`.
- Buffer AIO documentation treats synchronous inline transfer as the supported path under `io_method = sync`.
- Selecting `io_uring` is not an exposed option (no `IOMETHOD_IO_URING_ENABLED` build); resolving that method panics with a message to run `io_method = sync`.

Related: `io_workers` (default `3`) applies only when `io_method=worker`.

### Failure without override

Leaving the boot default (`worker`) has historically broken postmaster start in harnesses that do not inject config. Recovery TAP writes:

```conf
io_method = sync
max_stack_depth = 7000kB
```

into `TEMP_CONFIG` for every `PostgreSQL::Test::Cluster` node.

## Stack depth: GUC, Rust, and OS rlimit

Three independent limits interact. Raise **all** of them for multi-user / regression work.

### `max_stack_depth`

| Field | Value |
| --- | --- |
| Name | `max_stack_depth` |
| Type | integer, unit **kB** (`GUC_UNIT_KB`) |
| Context | `PGC_SUSET` |
| Boot default | **`2048`** (2 MiB) — intentional pgrust divergence from C’s `100` |
| Min / max | `100` … `MAX_KILOBYTES` (`2147483647`) |
| Hooks | `check_max_stack_depth`, `assign_max_stack_depth` |

C default 100 kB is too small for pgrust: larger Rust frames, seam indirection, and panic-based `ereport(ERROR)` chains overflow on deep catalog work (for example `\d` can fail with `stack depth limit exceeded`). Boot value `2048` matches the sample-config recommendation.

Harness overrides:

| Consumer | Value |
| --- | --- |
| README / Docker / main regress / isolation | `60000` (kB) |
| recovery / auth / ldap TAP `TEMP_CONFIG` | `7000kB` |

`check_max_stack_depth` rejects values that would leave less than `STACK_DEPTH_SLOP` (512 KiB) under the process stack rlimit, with hint:

> Increase the platform's stack depth limit via "ulimit -s" or local equivalent.

### `RUST_MIN_STACK`

| Field | Value |
| --- | --- |
| Name | `RUST_MIN_STACK` |
| Kind | process environment (Rust runtime) |
| Default in-tree | `33554432` bytes (32 MiB) |

Set **before** starting the binary. Docker entrypoint: `export RUST_MIN_STACK="${RUST_MIN_STACK:-33554432}"`. Regression and isolation scripts export the same default.

This is not a GUC. It sizes Rust thread stacks independently of Postgres’s `max_stack_depth` accounting.

### `ulimit -s`

| Field | Value |
| --- | --- |
| Command | `ulimit -s 65520` |
| Meaning | soft `RLIMIT_STACK` in **kB** (shell units) |

Entrypoint and harnesses run `ulimit -s 65520 2>/dev/null || true` so restricted environments do not abort. Without enough OS stack headroom, raising `max_stack_depth` fails the GUC check hook even when the number would otherwise be valid.

Backend code also re-anchors stack base at `PostgresMain` command-loop entry so inherited postmaster depth does not consume the entire GUC budget on the first statement.

## Network and listen settings

### `listen_addresses`

| Field | Value |
| --- | --- |
| Name | `listen_addresses` |
| Type | string list (`GUC_LIST_INPUT`) |
| Context | `PGC_POSTMASTER` |
| Boot default | `localhost` |
| CLI | `-c listen_addresses=…`, `-h VALUE`, `-i` → `*` |

| Mode | Setting | Use |
| --- | --- | --- |
| Local socket-only | `listen_addresses=` (empty) | README / regression: TCP off; connect via Unix socket (`-k` / `unix_socket_directories`) |
| Docker public server | `listen_addresses='*'` | Final entrypoint `exec` |
| Docker temp init server | `listen_addresses=''` | First-boot catalog setup only |

### Related listen knobs

| Name | Boot default | Notes |
| --- | --- | --- |
| `port` | `5432` (`DEF_PGPORT`) | CLI `-p` |
| `unix_socket_directories` | `/tmp` | CLI `-k`; Docker forces `/var/run/postgresql` for Debian `psql` defaults |
| `unix_socket_permissions` | `0777` | Postmaster |

Empty `listen_addresses` with `-k /tmp -p 5432` is the documented local-dev pattern; connect with `psql -h /tmp -p 5432 …`.

## Build-time `PGRUST_*` path env vars

Install paths are **compile-time** strings from `option_env!("PGRUST_…")` in `boot_paths` (and timezone resolution). They are **not** runtime env vars after the binary is linked.

| Build env var | Default if unset | Role |
| --- | --- | --- |
| `PGRUST_PGSHAREDIR` | `/usr/local/pgsql/share` | Share dir: templates, samples, **timezone / timezonesets** |
| `PGRUST_PGBINDIR` | `/usr/local/pgsql/bin` | Anchor for relative path derivation |
| `PGRUST_PKGLIBDIR` | `/usr/local/pgsql/lib` | Package lib dir |
| `PGRUST_SYSCONFDIR` | `/usr/local/pgsql/etc` | Sysconf |
| `PGRUST_INCLUDEDIR` | `/usr/local/pgsql/include` | Includes |
| `PGRUST_PKGINCLUDEDIR` | `/usr/local/pgsql/include` | Package includes |
| `PGRUST_INCLUDEDIRSERVER` | `/usr/local/pgsql/include/server` | Server includes |
| `PGRUST_LIBDIR` | `/usr/local/pgsql/lib` | Lib dir |
| `PGRUST_LOCALEDIR` | `/usr/local/pgsql/share/locale` | Locale |
| `PGRUST_DOCDIR` | `/usr/local/pgsql/share/doc` | Docs |
| `PGRUST_HTMLDIR` | `/usr/local/pgsql/share/doc` | HTML docs |
| `PGRUST_MANDIR` | `/usr/local/pgsql/share/man` | Man pages |

`.cargo/config.toml` bakes a workspace default:

```toml
[env]
PGRUST_PGSHAREDIR = "/tmp/pgrust_share"
```

`force` is false: an explicit shell export overrides the cargo config. README’s recommended source build:

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
cargo build --release --locked --bin postgres
```

Docker build sets `ENV PGRUST_PGSHAREDIR=${PG_SHAREDIR}` (`/usr/share/postgresql/18`) so the image binary’s tz tree matches the packaged share; a symlink maps `${PG_SHAREDIR}/timezone` → `/usr/share/zoneinfo` when the PGDG package omits zoneinfo.

### Runtime share override for initdb

Baked `PGRUST_PGSHAREDIR` does not replace `--initdb -L <share>` when the binary install layout cannot derive the vendored share tree. Always pass `-L` for out-of-tree / clone layouts:

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  ...
```

Wrong share/tz paths break boot (missing timezone data or bootstrap SQL).

## How to set configuration

### Command line

| Form | Effect |
| --- | --- |
| `-c name=value` | Set GUC from argv (`PGC_S_ARGV` when secure) |
| `--name=value` | Same long-option path |
| `-h ADDR` | `listen_addresses` |
| `-i` | `listen_addresses=*` |
| `-k DIR` | `unix_socket_directories` |
| `-p N` | `port` |
| `-D DIR` | Data directory (not a GUC write; postmaster data dir) |
| `-F` | `fsync=false` (regress / local convenience) |

`postgres --describe-config` dumps the full visible GUC catalog and exits.

### Config file / TAP

| Mechanism | Example |
| --- | --- |
| `postgresql.conf` | `io_method = sync` |
| Recovery/auth TAP | `TEMP_CONFIG` with `io_method` + `max_stack_depth` |

`io_method` and `listen_addresses` are postmaster-context: change requires restart, not `SIGHUP` alone.

### Docker injection

`PGRUST_SERVER_OPTS` always includes:

```text
-c io_method=sync
-c max_stack_depth=60000
-c unix_socket_directories=/var/run/postgresql
```

Final server: those opts + `-c listen_addresses='*'`. User CMD flags are appended after.

## Quick verification

| Check | Signal |
| --- | --- |
| Server accepts connections | `psql` connects; `SELECT version()` |
| I/O method | `SHOW io_method;` → `sync` when launched with override |
| Stack GUC | `SHOW max_stack_depth;` matches launch override |
| Listen | Empty string → Unix socket only; `*` / host → TCP |
| Share/tz | No timezone load errors at start; initdb completes with success text |

## Common failure modes

| Symptom | Likely misconfig |
| --- | --- |
| Postmaster panic / AIO method error at start | Missing `-c io_method=sync` |
| `stack depth limit exceeded` on trivial or catalog queries | Low `max_stack_depth`, missing `RUST_MIN_STACK`, or small `ulimit -s` |
| GUC reject for `max_stack_depth` with ulimit hint | OS rlimit too low for requested kB + 512 KiB slop |
| Wrong timezone / missing share files | Bad bake-in `PGRUST_PGSHAREDIR` or missing initdb `-L` |
| Clients cannot connect on TCP | `listen_addresses=` empty; use socket path or set addresses / `-i` |
| Docker `psql` cannot find socket | Expect `/var/run/postgresql` (entrypoint overrides boot `/tmp`) |

## Related pages

<CardGroup>
  <Card title="Run the server" href="/run-server">
    Start postmaster with `-D`, socket/TCP flags, and the mandatory stack + `io_method` profile.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    `PGRUST_PGSHAREDIR`, runtime `-L`, vendored share tree, and tz path failures.
  </Card>
  <Card title="postgres CLI reference" href="/cli-reference">
    Flags, must-be-first dispatch options, and `-c` / `--NAME=VALUE` GUC overrides.
  </Card>
  <Card title="Docker environment reference" href="/docker-reference">
    `POSTGRES_*` env, init scripts, and injected server opts.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Known failure modes: missing `io_method=sync`, stack overflows, wrong share/tz paths.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    Shortest local path: build, initdb with `-L`, start with required settings, connect with `psql`.
  </Card>
</CardGroup>

---

## 17. Docker environment reference

> POSTGRES_PASSWORD, POSTGRES_USER, POSTGRES_DB, POSTGRES_INITDB_ARGS, POSTGRES_HOST_AUTH_METHOD, PGDATA, init scripts under /docker-entrypoint-initdb.d, and injected server opts.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/17-docker-environment-reference.md
- Generated: 2026-07-10T05:31:37.138Z

### Source Files

- `Dockerfile`
- `docker/entrypoint.sh`
- `docker/README.md`
- `README.md`
- `.dockerignore`

---
title: "Docker environment reference"
description: "POSTGRES_PASSWORD, POSTGRES_USER, POSTGRES_DB, POSTGRES_INITDB_ARGS, POSTGRES_HOST_AUTH_METHOD, PGDATA, init scripts under /docker-entrypoint-initdb.d, and injected server opts."
---

The root `Dockerfile` and `docker/entrypoint.sh` implement an official-postgres-shaped image contract: env vars for first-boot identity and auth, `PGDATA` volume layout, `/docker-entrypoint-initdb.d` scripts, `ENTRYPOINT ["docker-entrypoint.sh"]` / `CMD ["postgres"]`, gosu step-down to uid 999, `EXPOSE 5432`, and `STOPSIGNAL SIGINT`. The server and catalog bootstrap are both the pgrust binary at `/usr/local/bin/pgrust-postgres` (`--initdb` plus postmaster); the only C tool retained is PGDG PostgreSQL 18 `psql` for init SQL and healthchecks.

## Contract surface

| Surface | Value / behavior |
| --- | --- |
| Image entrypoint | `docker-entrypoint.sh` (also linked as `/docker-entrypoint.sh`) |
| Default command | `postgres` (rewritten to `pgrust-postgres`) |
| Server + initdb binary | `/usr/local/bin/pgrust-postgres` (`PGRUST_BIN`) |
| Client | `psql` on `PATH` under `/usr/lib/postgresql/18/bin` |
| Share tree | `PG_SHAREDIR` default `/usr/share/postgresql/18` (passed to `--initdb` via `-L`) |
| Data volume | `VOLUME /var/lib/postgresql/data`, `ENV PGDATA=/var/lib/postgresql/data` |
| Socket dir | `/var/run/postgresql` (owned for `postgres`, mode 3777 at image build) |
| User | `postgres` uid/gid 999 |
| Port | 5432 |
| Locale | `LANG=en_US.utf8` |
| Stop | `SIGINT` (fast shutdown; temp init server also stopped with SIGINT) |

```bash
docker build -t pgrust .
docker run --rm -e POSTGRES_PASSWORD=secret -p 5432:5432 pgrust
```

Published image example: `malisper/pgrust:v0.1` with the same env contract.

## Environment variables

`docker_setup_env` loads the official-style `POSTGRES_*` vars (with `_FILE` secret support) before any init or server start when argv is `postgres` (or flags that become `postgres …`).

### `file_env` and Docker secrets

`file_env VAR [DEFAULT]` supports either `VAR` or `VAR_FILE` (path whose contents become the value). Setting both exits with:

```text
error: both VAR and VAR_FILE are set (but are exclusive)
```

Applied to: `POSTGRES_PASSWORD`, `POSTGRES_USER`, `POSTGRES_DB`, `POSTGRES_INITDB_ARGS`.

### Primary variables

<ParamField body="POSTGRES_PASSWORD" type="string" required>
Superuser password. Required on first boot unless `POSTGRES_HOST_AUTH_METHOD=trust`. Empty password + non-trust auth exits with *Database is uninitialized and superuser password is not specified.* After initdb, set via `ALTER ROLE CURRENT_USER WITH PASSWORD …` on a temporary server (`docker_setup_password`) because pgrust `--initdb` has no `--pwfile`. Also used as default `PGPASSWORD` during init-script SQL if unset.
</ParamField>

<ParamField body="POSTGRES_USER" type="string" default="postgres">
Bootstrap superuser name. Passed to `--initdb` as `--username`. Used by `psql` for init SQL (`--username`).
</ParamField>

<ParamField body="POSTGRES_DB" type="string" default="same as POSTGRES_USER">
Application database name. Created after the temp server starts if missing (`CREATE DATABASE`). Init SQL runs against this db when set; catalog checks for existence use the `postgres` database with `POSTGRES_DB` temporarily cleared.
</ParamField>

<ParamField body="POSTGRES_INITDB_ARGS" type="string" default="empty">
Extra argv spliced into the `--initdb` invocation via shell `eval` (space-separated options only). Always preceded by fixed flags: `-D "$PGDATA" -L "$PG_SHAREDIR" --username "$POSTGRES_USER"`.
</ParamField>

<ParamField body="POSTGRES_HOST_AUTH_METHOD" type="string" default="trust (at pg_hba write time)">
Appended to `$PGDATA/pg_hba.conf` as `host all all all <method>`. Empty until `pg_setup_hba_conf`, which defaults to `trust` for TCP because password host auth is not yet enforced by pgrust (see below). Explicit values always win; non-`trust`/`reject` methods emit a pgrust-specific WARNING.
</ParamField>

<ParamField body="PGDATA" type="string" default="/var/lib/postgresql/data">
Data directory. Created with mode `0700` when possible; ownership fixed to `postgres` when the entrypoint runs as root. First-boot detection: non-empty `$PGDATA/PG_VERSION` sets `DATABASE_ALREADY_EXISTS` and skips init.
</ParamField>

### Related variables

| Variable | Default | Role |
| --- | --- | --- |
| `POSTGRES_INITDB_WALDIR` | unset | If set, creates/chowns the WAL directory and passes `--waldir` to `--initdb` |
| `POSTGRES_PASSWORD_FILE` / `POSTGRES_USER_FILE` / … | unset | Docker secrets via `file_env` |
| `PGPASSWORD` | falls back to `POSTGRES_PASSWORD` during first-boot SQL | Export for `psql` if local HBA needs a password |
| `PGPORT` | `5432` | Temp server port (`-p`) during init |
| `PG_MAJOR` | `18` (image `ENV`) | Old-layout detection when `PGDATA` is `/var/lib/postgresql/$PG_MAJOR/docker` |
| `PGRUST_BIN` | `/usr/local/bin/pgrust-postgres` | Absolute path to server/initdb binary |
| `PG_SHAREDIR` | `${PGRUST_PGSHAREDIR:-/usr/share/postgresql/18}` | Share dir for `-L` and bootstrap templates |
| `PGRUST_PGSHAREDIR` | baked at image build (`/usr/share/postgresql/18`) | Tz tree path in the binary; also fallback for `PG_SHAREDIR` |
| `RUST_MIN_STACK` | `33554432` | Exported every entrypoint run |
| `LANG` | `en_US.utf8` | Image locale |
| `PATH` / `LD_LIBRARY_PATH` | PGDG bin + `/opt/runlibs` | `psql` and its non-glibc deps |

## First-boot lifecycle

Runs only when argv is `postgres` (not help/version) and `$PGDATA/PG_VERSION` is absent.

```text
root? ──gosu postgres──┐
                       ▼
docker_setup_env → docker_create_db_directories
                       ▼
docker_verify_minimum_env → docker_error_old_databases
                       ▼
docker_init_database_dir   # pgrust-postgres --initdb …
                       ▼
pg_setup_hba_conf          # append host line
                       ▼
docker_temp_server_start   # socket-only, listen_addresses=''
  docker_setup_password
  docker_setup_db
  docker_process_init_files /docker-entrypoint-initdb.d/*
docker_temp_server_stop
                       ▼
exec pgrust-postgres -D $PGDATA [PGRUST_SERVER_OPTS] -c listen_addresses='*' …
```

**Success signal (stdout):**

```text
PostgreSQL init process complete; ready for start up.
```

**Existing data dir (skip init):**

```text
PostgreSQL Database directory appears to contain a database; Skipping initialization
```

Then the same final `exec` of the pgrust server.

### Initdb invocation

```bash
"$PGRUST_BIN" --initdb -D "$PGDATA" -L "$PG_SHAREDIR" --username "$POSTGRES_USER" $POSTGRES_INITDB_ARGS …
```

| Constraint | Detail |
| --- | --- |
| Option form | Space-separated (`--opt value`), not `--opt=value` |
| Supported subset | `-D`/`--pgdata`, `-U`/`--username`, `-L`, `-E`/`--encoding`, locale flags, `--wal-segsize`, plus entrypoint `--waldir` when `POSTGRES_INITDB_WALDIR` is set |
| Unsupported in args | e.g. `-A`/`--auth`, `--data-checksums` — error at initdb time |
| Password | Not on the initdb CLI; applied later with `ALTER ROLE` |
| Superuser source | `--username` only (no nss_wrapper / `/etc/passwd` lookup) |

Creates the usual `postgres`, `template0`, and `template1` databases; superuser name comes from `POSTGRES_USER`.

### Host auth and password reality

| Behavior | Detail |
| --- | --- |
| Catalog password | Still stored when `POSTGRES_PASSWORD` is non-empty |
| Default host HBA | `host all all all trust` unless `POSTGRES_HOST_AUTH_METHOD` overrides |
| Password methods (`scram-sha-256`, `md5`, …) | TCP logins hit unported `fetch_role_password` → FATAL *seam not installed*; entrypoint warns if those methods are forced |
| Local / unix socket | Init client uses socket under `/var/run/postgresql` with trust-friendly defaults for setup |

Empty password is allowed only with `POSTGRES_HOST_AUTH_METHOD=trust`. Trust always prints the large upstream-style WARNING on first boot.

### Old data layout (PG 18+)

If `PGDATA` is `/var/lib/postgresql/$PG_MAJOR/docker` and leftover `PG_VERSION` trees (or an unused `/var/lib/postgresql/data` mount) are found, init aborts with the docker-library “in 18+, … pg_ctlcluster” error and suggests mounting at `/var/lib/postgresql` for upgrade-friendly layout.

## Init scripts: `/docker-entrypoint-initdb.d`

First boot only. Directory must be listable (`ls /docker-entrypoint-initdb.d/`). Scripts run against a **socket-only** temp server (`listen_addresses=''`) as `POSTGRES_USER`, default db `POSTGRES_DB` when set.

| Pattern | Action |
| --- | --- |
| `*.sh` (executable) | Execute |
| `*.sh` (not executable) | Source into the entrypoint shell |
| `*.sql` | `psql -f` |
| `*.sql.gz` | `gunzip -c` → `psql` |
| `*.sql.xz` | `xzcat` → `psql` |
| `*.sql.zst` | `zstd -dc` → `psql` |
| other | Logged as ignored |

`psql` flags: `-v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password --no-psqlrc`, with `PGHOST`/`PGHOSTADDR` cleared so the default socket path applies. Image includes `gzip`, `xz-utils`, and `zstd` for compressed SQL.

```bash
docker run --rm \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_USER=app \
  -e POSTGRES_DB=appdb \
  -v "$PWD/initdb.d:/docker-entrypoint-initdb.d:ro" \
  -p 5432:5432 \
  pgrust
```

## Injected server options

Applied to **both** the temporary init server and the final postmaster. User CMD flags are preserved after these opts.

| Source | Setting |
| --- | --- |
| `ulimit -s` | `65520` (best-effort; failures ignored) |
| `RUST_MIN_STACK` | `33554432` if unset |
| `PGRUST_SERVER_OPTS` | `-c io_method=sync` |
| | `-c max_stack_depth=60000` |
| | `-c unix_socket_directories=/var/run/postgresql` |
| Temp server only | `-c listen_addresses=''` |
| Final server only | `-c listen_addresses='*'` |

Final rewrite when CMD is `postgres` (not help/version):

```bash
exec "$PGRUST_BIN" -D "$PGDATA" \
  -c io_method=sync \
  -c max_stack_depth=60000 \
  -c unix_socket_directories=/var/run/postgresql \
  -c listen_addresses='*' \
  "$@"
```

Non-`postgres` commands (for example an explicit `psql`, or help/version paths) are `exec`’d unchanged. Leading flags alone (`docker run … pgrust -c …`) are rewritten as `postgres -c …` first.

`io_method=sync` and the stack settings are required for pgrust boot; the entrypoint injects them so the official `CMD ["postgres"]` contract still works without callers adding flags.

## Image paths and build wiring

| Path | Content |
| --- | --- |
| `/usr/local/bin/pgrust-postgres` | pgrust release binary (server + `--initdb`) |
| `/usr/lib/postgresql/18/bin/psql` | C client only |
| `/usr/share/postgresql/18` | Bootstrap templates, config samples; `timezone` → `/usr/share/zoneinfo` symlink |
| `/var/lib/postgresql/data` | Default `PGDATA` volume |
| `/var/run/postgresql` | Unix sockets (aligned with Debian `psql`) |
| `/docker-entrypoint-initdb.d` | Empty dir for first-init scripts |
| `/usr/local/bin/docker-entrypoint.sh` | Entrypoint |

Build bake-in: `PGRUST_PGSHAREDIR=/usr/share/postgresql/18` during `cargo build --release --locked --bin postgres`. No C `postgres` or C `initdb` in the final image.

`.dockerignore` keeps the build context lean (drops `/target`, `/docs`, `/scripts`, `.git`, etc.) while keeping crate sources and fixtures needed to compile.

## Verification

<Check>
After first boot, logs include `PostgreSQL init process complete; ready for start up.` Then:

```bash
psql postgres://postgres:secret@localhost:5432/postgres \
  -c "select version(), 1 + 1 as two"
```

Or in-container: `docker exec -e PGPASSWORD=secret <ctr> psql -h 127.0.0.1 -U postgres -c '\q'`.
</Check>

## Failure modes

| Symptom | Cause / fix |
| --- | --- |
| Exit: superuser password not specified | Set `POSTGRES_PASSWORD` or `POSTGRES_HOST_AUTH_METHOD=trust` |
| FATAL *seam not installed: fetch_role_password* on TCP | Host HBA is password-based; use default/trust host auth until password auth is ported |
| Unsupported initdb option error | Strip `-A`, `--data-checksums`, or other unsupported `POSTGRES_INITDB_ARGS`; use space-separated form |
| `both X and X_FILE are set` | Pass only the env var or only the `_FILE` secret |
| Temp server timeout / exit during startup | Check `PGDATA` permissions, share dir (`-L` / `PG_SHAREDIR`), stack/`io_method` injection path |
| Old databases error on 18+ layout | Re-home volume mounts per the entrypoint message; do not leave stray `PG_VERSION` trees under `/var/lib/postgresql` |
| Init scripts not run | Only on empty `PGDATA` (no `PG_VERSION`); wrong mount or pre-existing volume |
| Socket connection failures during init | `unix_socket_directories` must be `/var/run/postgresql` (entrypoint injects this) |

## Related pages

<CardGroup>
  <Card title="Docker" href="/docker">
    Run the published or locally built image and confirm first-boot init.
  </Card>
  <Card title="initdb options reference" href="/initdb-reference">
    Supported `--initdb` flags and space-separated parsing used by `POSTGRES_INITDB_ARGS`.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `io_method`, stack settings, listen addresses, and build-time path env vars.
  </Card>
  <Card title="Run the server" href="/run-server">
    Non-Docker postmaster start with the same mandatory GUCs.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Missing `io_method=sync`, stack overflows, share/tz path failures, and related boot issues.
  </Card>
</CardGroup>

---

## 18. Test harness environment reference

> Environment variables and defaults for run-regression, serial/pg_regress, isolation, auth/ldap TAP, and recovery TAP: PGRUST_BIN, PGRUST_PSQL, PG_SRC, PG_INSTALL, ports, OUTDIR.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/18-test-harness-environment-reference.md
- Generated: 2026-07-10T05:32:14.588Z

### Source Files

- `scripts/run-regression`
- `scripts/run-pg-regress-serial`
- `scripts/run-pg-isolation`
- `scripts/run-auth-tap`
- `scripts/run-recovery-tap`
- `scripts/run-ldap-tap`

---
title: "Test harness environment reference"
description: "Environment variables and defaults for run-regression, serial/pg_regress, isolation, auth/ldap TAP, and recovery TAP: PGRUST_BIN, PGRUST_PSQL, PG_SRC, PG_INSTALL, ports, OUTDIR."
---

`scripts/run-regression`, `scripts/run-pg-regress-serial` (via `scripts/run-pg-regress`), `scripts/run-pg-isolation`, `scripts/run-auth-tap`, `scripts/run-ldap-tap`, and `scripts/run-recovery-tap` read a small set of environment variables to locate the pgrust `postgres` binary, a PostgreSQL 18 client or C install, SQL/expected sources, ports, and output directories. Clone-only regression uses vendored `vendor/postgres-18.3/` and needs only a `psql` client; serial, isolation, and TAP harnesses require an external Postgres 18.3 source tree and/or C install.

## Harness matrix

| Script | Role | Needs `PG_SRC` | Needs `PG_INSTALL` | Default `PGPORT` | Default `OUTDIR` pattern |
| --- | --- | --- | --- | --- | --- |
| `scripts/run-regression` | Clone-only main regress | No (vendored) | No | `55434` | `$TMPDIR/pgrust_regress.XXXXXX` |
| `scripts/run-pg-regress` | Thin wrapper → serial | Yes (required) | Yes (required) | (via serial) | (via serial) |
| `scripts/run-pg-regress-serial` | Serial SQL vs C tools | Yes (required) | Yes (required) | `55433` | `$TMPDIR/pgrust_pg_regress_serial.XXXXXX` |
| `scripts/run-pg-isolation` | Isolation schedule | Yes (required) | Yes (required) | `55434` | `$TMPDIR/pgrust_pg_isolation.XXXXXX` |
| `scripts/run-auth-tap` | Auth TAP via `prove` | Yes (defaulted) | Yes (defaulted) | (Cluster ports) | fixed `TESTDATADIR` |
| `scripts/run-ldap-tap` | LDAP TAP via `prove` | Yes (defaulted) | Yes (defaulted) | (Cluster ports) | fixed `TESTDATADIR` |
| `scripts/run-recovery-tap` | Recovery TAP via `prove` | Yes (defaulted) | Yes (defaulted) | (Cluster ports) | fixed `TESTDATADIR` |

`$TMPDIR` falls back to `/tmp` when unset. Tempdir names use `mktemp -d`.

## Shared binary and path variables

<ParamField body="PGRUST_BIN" type="path" default="&lt;repo&gt;/target/release/postgres">
Path to the pgrust `postgres` executable. Every harness checks `-x` and exits with a build hint if missing: `cargo build --release --bin postgres`.
</ParamField>

<ParamField body="PGRUST_PSQL" type="path" optional>
**`run-regression` only.** Explicit `psql` client. Resolution order when unset: `command -v psql`, then `/tmp/pgrust_pginstall/bin/psql`. Exit if none is executable.
</ParamField>

<ParamField body="PG_SRC" type="path">
PostgreSQL **18.3 source tree**.

- **Required** for `run-pg-regress-serial` / `run-pg-regress` and `run-pg-isolation` (no default; script exits if unset).
- **Defaulted** for TAP scripts: `$repo_root/../../pgrust/postgres-18.3`, then fallback `$HOME/workspace/work/pgrust/postgres-18.3` if the suite directory is missing under the first path.

Suite probes: `src/test/regress` (serial), `src/test/isolation` (isolation), `src/test/authentication` / `ldap` / `recovery` (TAP).
</ParamField>

<ParamField body="PG_INSTALL" type="path">
Full C PostgreSQL 18.3 install prefix (`bin/`, `lib/`, `share/`).

- **Required** for serial and isolation (must provide `bin/initdb`, `bin/psql`; serial also needs `bin/createdb`; isolation needs `initdb`/`psql` only).
- **Default** for TAP: `/tmp/pgrust_pginstall`. TAP requires executable `bin/initdb` and directory `share/postgresql`.
</ParamField>

<ParamField body="PG_BUILD" type="path" default="PG_SRC">
**`run-pg-isolation` only.** Build tree that may contain `src/test/isolation/pg_isolation_regress`.
</ParamField>

<ParamField body="PG_ISOLATION_REGRESS" type="path" optional>
**`run-pg-isolation` only.** Explicit path to `pg_isolation_regress`. If unset, searches `$PG_BUILD/src/test/isolation/pg_isolation_regress` then `$PG_SRC/src/test/isolation/pg_isolation_regress`.
</ParamField>

## Ports, output, and debug stops

| Variable | Harnesses | Default | Notes |
| --- | --- | --- | --- |
| `PGPORT` | regression, serial, isolation | `55434` (regression, isolation); `55433` (serial) | Unix socket only (`listen_addresses=` empty). TAP clusters choose their own ports via PostgreSQL::Test::Cluster. |
| `OUTDIR` | regression, serial, isolation | `mktemp` under `$TMPDIR` (see matrix) | Holds `data/`, `postmaster.log`, `initdb.log`, per-test `*.out`, `_status.tsv` (SQL runners), `results/` (SQL runners). Isolation also uses `$OUTDIR/output_iso`. |
| `STOP_AFTER` | regression, serial | unset | When set to a test name, stop after that file (debugging). |
| `TMPDIR` | regression, serial, isolation | `/tmp` | Base for default `OUTDIR`. |

### Layout under `OUTDIR` (SQL runners)

```text
$OUTDIR/
  data/              # PGDATA from initdb / --initdb
  postmaster.log
  initdb.log
  results/           # server-side COPY / lo_export targets
  _status.tsv        # name, status, diff-lines, best expected
  _order.txt         # run order
  <test>.out         # actual psql output
  sock/              # serial/isolation socket dir (under OUTDIR)
```

`run-regression` places the Unix socket in a **short** path `/tmp/pgr.XXXXXX` (not under `OUTDIR`) because of the macOS `sun_path` length limit. Serial and isolation keep the socket at `$OUTDIR/sock`.

## Client, locale, and stack environment (SQL harnesses)

Exported by `run-regression`, `run-pg-regress-serial`, and `run-pg-isolation` (unless already set):

| Variable | Default | Purpose |
| --- | --- | --- |
| `PGTZ` | `America/Los_Angeles` | Match Postgres regress expected timestamps |
| `PGDATESTYLE` | `Postgres, MDY` | Date output style |
| `LANG` | `C` | Locale for messages |
| `LC_MESSAGES` | `C` | Message catalog locale |
| `PGOPTIONS` | `-c intervalstyle=postgres_verbose` | Session GUC for interval formatting |
| `RUST_MIN_STACK` | `33554432` (32 MiB) | Rust thread stack for deep recursion |

Also: `unset LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC LC_TIME LANGUAGE` so partial locale env does not skew diffs.

`run-regression` / serial additionally set:

- `PG_ABS_SRCDIR` → regress directory (`vendor/.../regress` or `$PG_SRC/src/test/regress`)
- serial: `PG_LIBDIR=$PG_INSTALL/lib/postgresql`, `PG_DLSUFFIX` (`.dylib` on Darwin, else `.so` if unset)

Postmaster command line (SQL harnesses): `-c io_method=sync -c max_stack_depth=60000`, plus `ulimit -s 65520` when allowed. Checkpoint/autovacuum/replication worker GUCs are fixed in-script, not env-driven.

## `run-regression` (clone-only)

**Prerequisites:** release `postgres`, vendored tree `vendor/postgres-18.3/` (`share/postgres.bki`, `regress/sql`, `regress/expected`, `regress/parallel_schedule`), PostgreSQL 18 `psql`.

**Not used:** `PG_SRC`, `PG_INSTALL`.

**Init:** `"$PGRUST_BIN" --initdb -D "$DATADIR" -L "$SHARE" --no-locale --encoding UTF8 -U postgres` with `SHARE=$repo_root/vendor/postgres-18.3/share`.

**psql vars:** `abs_srcdir=$REGRESS`, `abs_builddir=$OUTDIR`, `libdir='$libdir'`, `dlsuffix=.so` (regress helpers resolve as builtin `regress`, not a real `.so` path).

**Args:** no args → all names from `parallel_schedule` `test:` lines; args → those tests with `test_setup` auto-prepended if missing.

**Status values:** `PASS`, `FAIL`, `PSQL_FAIL`, `CRASHED` in `_status.tsv`. Exit non-zero if any non-`PASS`.

<RequestExample>
```bash
# full suite
scripts/run-regression

# subset + keep output
OUTDIR=/tmp/my-regress PGPORT=55434 \
  PGRUST_BIN="$PWD/target/release/postgres" \
  PGRUST_PSQL="$(brew --prefix libpq)/bin/psql" \
  scripts/run-regression select numeric

# stop after one test
STOP_AFTER=select scripts/run-regression select insert
```
</RequestExample>

## Serial `pg_regress` (`run-pg-regress` / `run-pg-regress-serial`)

`run-pg-regress` execs `run-pg-regress-serial`.

**Required:** `PG_SRC`, `PG_INSTALL` (with `initdb`, `psql`, `createdb`).

**Init:** C `"$PG_INSTALL/bin/initdb"` (not pgrust `--initdb`). Server is still `$PGRUST_BIN`. Client tools are always from `$PG_INSTALL/bin`.

**Default port `55433`** (differs from clone-only / isolation `55434`).

**libdir:** `-v libdir=$PG_INSTALL/lib/postgresql` (C extension path model; not the clone-only `$libdir` token).

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/tmp/pgrust_pginstall
scripts/run-pg-regress-serial boolean int4
# or
scripts/run-pg-regress
```

## Isolation (`run-pg-isolation`)

**Required:** `PG_SRC`, `PG_INSTALL`, and a built `pg_isolation_regress` (via `PG_ISOLATION_REGRESS` or `PG_BUILD`/`PG_SRC` search).

**Init:** C `initdb`; server `$PGRUST_BIN`; then execs `pg_isolation_regress --use-existing` with host=`$SOCK`, port=`$PGPORT` (default `55434`), `--bindir=$PG_INSTALL/bin`, schedule `$PG_SRC/src/test/isolation/isolation_schedule`, output `$OUTDIR/output_iso`.

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/tmp/pgrust_pginstall
export PG_BUILD=/path/to/postgres-18.3/build   # if isolation tool is only there
scripts/run-pg-isolation
```

## TAP harnesses (auth / ldap / recovery)

All three: assemble a **fake install** under a fixed path, put real copies of C `pg_ctl` and pgrust `postgres` in `bin/` (so `realpath` does not escape to C postgres), symlink remaining tools to `$PG_INSTALL/bin/*`, set `PATH`/`TEMP_CONFIG`/`PG_REGRESS`/`TESTDATADIR`, then `exec prove -v`.

### TAP-shared environment

| Variable | Default | Role |
| --- | --- | --- |
| `PGRUST_BIN` | `<repo>/target/release/postgres` | Copied to fake `bin/postgres` |
| `PG_SRC` | sibling path then `$HOME/workspace/work/pgrust/postgres-18.3` | Suite + `src/test/perl` on `PERL5LIB` |
| `PG_INSTALL` | `/tmp/pgrust_pginstall` | C tools + share/lib |
| `PG_TEST_TIMEOUT_DEFAULT` | `90` | Cluster poll timeout (seconds) |
| `TEMP_CONFIG` | harness-written conf under `/tmp` | Forces `io_method = sync` and `max_stack_depth = 7000kB` |
| `PG_REGRESS` | `$PG_SRC/src/test/regress/pg_regress` | Cluster auth config helper |
| `TESTDATADIR` | harness-fixed under `/tmp` | Cluster data root |
| `PERL5LIB` | `$PG_SRC/src/test/perl` (+ optional local `IPC::Run`) | TAP modules |
| `DYLD_LIBRARY_PATH` / `LD_LIBRARY_PATH` | prepend `$PG_INSTALL/lib` | C client libs |

### Fixed install / data paths

| Harness | Fake install (`INST`) | `TEMP_CONFIG` file | `TESTDATADIR` |
| --- | --- | --- | --- |
| recovery | `/tmp/pgrust_recovery_tap_inst` | `/tmp/pgrust_recovery_tap_tempconfig.conf` | `/tmp/pgrust_recovery_tap_data` |
| auth | `/tmp/pgrust_auth_tap_inst` | `/tmp/pgrust_auth_tap_tempconfig.conf` | `/tmp/pgrust_auth_tap_data` |
| ldap | `/tmp/pgrust_ldap_tap_inst` | `/tmp/pgrust_ldap_tap_tempconfig.conf` | `/tmp/pgrust_ldap_tap_data` |

Each run `rm -rf`s its `INST` and `TESTDATADIR` (and local `tmp_check` / `log` under the suite directory) before recreate.

### Build-capability env (skip gates)

Unset configure flags would fatal-fail under TAP `use warnings FATAL => 'all'`. Scripts default:

| Variable | recovery / auth default | ldap default |
| --- | --- | --- |
| `with_icu` | `no` | `no` |
| `with_ssl` | `openssl_no` (any non-`openssl` skips SSL) | `openssl_no` |
| `with_gssapi` | `no` | `no` |
| `with_ldap` | `no` | `yes` |
| `with_pam` | `no` | `yes` |
| `with_readline` | `no` | `no` |
| `enable_injection_points` | `no` (auth only) | — |
| `PG_TEST_EXTRA` | — | `ldap` (enables slapd suite) |

### Default test files when argv empty

| Harness | Default tests |
| --- | --- |
| `run-recovery-tap` | `t/002_archiving.pl` `t/003_recovery_targets.pl` `t/020_archive_status.pl` `t/023_pitr_prepared_xact.pl` `t/024_archive_recovery.pl` `t/025_stuck_on_old_timeline.pl` `t/042_low_level_backup.pl` `t/045_archive_restartpoint.pl` |
| `run-auth-tap` | `t/003_peer.pl` `t/004_file_inclusion.pl` `t/006_login_trigger.pl` `t/007_pre_auth.pl` |
| `run-ldap-tap` | `t/001_auth.pl` |

### TAP self-check

Before `prove`, each TAP script resolves `postgres` the way `pg_ctl` does and requires the binary to contain the string `PGRUST_TRACE`. Failure prints `FATAL: pg_ctl would launch a NON-pgrust postgres` and exits — the historical bug was symlinking `pg_ctl` so `realpath` landed on C postgres.

```text
  $INST/bin/pg_ctl      = real copy of C pg_ctl   (realpath stays in INST)
  $INST/bin/postgres    = real copy of PGRUST_BIN
  $INST/bin/*           = symlinks → $PG_INSTALL/bin/*  (initdb/psql escape to C)
  $INST/share,lib       = symlinks → $PG_INSTALL
```

Server under test is pgrust; bootstrap (`initdb`) and clients stay on C tools.

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/tmp/pgrust_pginstall
export PGRUST_BIN="$PWD/target/release/postgres"
export PG_TEST_TIMEOUT_DEFAULT=120
scripts/run-recovery-tap t/002_archiving.pl
scripts/run-auth-tap
PG_TEST_EXTRA=ldap scripts/run-ldap-tap
```

## Server settings injected by harnesses

Not environment variables, but required for successful boots:

| Setting | SQL harnesses (CLI) | TAP (`TEMP_CONFIG`) |
| --- | --- | --- |
| `io_method` | `sync` | `sync` |
| `max_stack_depth` | `60000` (GUC units as passed) | `7000kB` |
| `listen_addresses` | empty (socket only) | (Cluster defaults) |
| `checkpoint_timeout` | `3600` | — |
| `autovacuum` | `on` | — |
| `max_logical_replication_workers` | `0` | — |

Without `io_method=sync`, pgrust panics at startup (worker IO unported).

## Failure modes

| Symptom | Likely cause / env |
| --- | --- |
| `pgrust postgres not found at ...` | Build missing or wrong `PGRUST_BIN` |
| `no psql client found` | Clone-only: set `PGRUST_PSQL` or install libpq client |
| `set PG_SRC` / `set PG_INSTALL` | Serial/isolation: exports required, no defaults |
| `C postgres source tree not found` | TAP: set `PG_SRC` to a tree with the suite dir |
| `full C install not found at PG_INSTALL=...` | TAP: need `bin/initdb` + `share/postgresql` |
| `pg_isolation_regress not found` | Build isolation tests or set `PG_ISOLATION_REGRESS` / `PG_BUILD` |
| `FATAL: pg_ctl would launch a NON-pgrust postgres` | Fake-install layout broken; do not replace real `pg_ctl`/`postgres` copies with pure symlinks |
| `postmaster did not start` | Check `$OUTDIR/postmaster.log` (often missing `io_method=sync` outside harness) |
| False TAP FAIL on skips | Capability env unset; harness defaults `with_*` / `enable_injection_points` intentionally |
| Socket connect failures on macOS | Clone-only already uses short `/tmp/pgr.*` socket dir; avoid long custom socket paths |

## Quick env cheat sheet

```bash
# Universal
PGRUST_BIN=$PWD/target/release/postgres

# Clone-only
PGRUST_PSQL=...   PGPORT=55434   OUTDIR=...   STOP_AFTER=...

# Serial / isolation
PG_SRC=...   PG_INSTALL=...   PGPORT=55433|55434   OUTDIR=...
PG_BUILD=...   PG_ISOLATION_REGRESS=...   # isolation only
STOP_AFTER=...   # serial only

# TAP
PG_SRC=...   PG_INSTALL=/tmp/pgrust_pginstall
PG_TEST_TIMEOUT_DEFAULT=90
PG_TEST_EXTRA=ldap            # ldap suite
# optional overrides of with_icu / with_ssl / with_ldap / enable_injection_points
```

## Related pages

<CardGroup>
  <Card title="Run regression tests" href="/regression-tests">
    Clone-only workflow, selective tests, and optional isolation/auth/recovery harness usage.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Runtime GUCs harnesses force: `io_method`, stack depth, and build-time path env vars.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    Share tree and `-L` wiring used by clone-only `--initdb`.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Missing `psql`, stack overflows, and `io_method=sync` failures outside harnesses.
  </Card>
  <Card title="Build from source" href="/build-from-source">
    Produce `target/release/postgres` expected by `PGRUST_BIN` defaults.
  </Card>
  <Card title="Compatibility and status" href="/compatibility">
    Regression-oracle model and non-production limits that shape these suites.
  </Card>
</CardGroup>

---

## 19. Build from source

> Host packages, PGRUST_PGSHAREDIR bake-in, cargo build --release --locked --bin postgres, wasm64 single-user notes, and binary location target/release/postgres.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/19-build-from-source.md
- Generated: 2026-07-10T05:32:20.560Z

### Source Files

- `README.md`
- `Cargo.toml`
- `.cargo/config.toml`
- `crates/_support/seam/init/Cargo.toml`
- `crates/wasm-libc-shim/src/lib.rs`
- `Dockerfile`

---
title: "Build from source"
description: "Host packages, PGRUST_PGSHAREDIR bake-in, cargo build --release --locked --bin postgres, wasm64 single-user notes, and binary location target/release/postgres."
---

The release server binary is the `postgres` bin from workspace package `init` (`crates/_support/seam/init`): one `[[bin]]` entry that creates the top memory context, runs `init::init_all` seam registration, then dispatches through `main_main::pg_main`. Native builds use `cargo build --release --locked --bin postgres` and emit `target/release/postgres`. Install-time share and path defaults are compile-time `option_env!("PGRUST_*")` values (primarily `PGRUST_PGSHAREDIR`) read from `backend/utils/init/miscinit` boot path helpers.

## Prerequisites

### Toolchain

| Requirement | Notes |
| --- | --- |
| Rust toolchain + Cargo | Workspace `edition = "2021"`. No `rust-version` pin in the root workspace. |
| `Cargo.lock` | Use `--locked` so the tree matches the committed lockfile. |
| Host C libraries | OpenSSL, ICU, LDAP, PAM (and related system libs) for native link of the ported backend FFI crates. |
| Share tree | Vendored Postgres 18.3 files under `vendor/postgres-18.3/share` (or an equivalent share dir at the path baked into the binary). |

`postgresql-client-18` / Homebrew `libpq` supply a Postgres 18 `psql` for connect and regression work; they are not required to link the server binary itself.

### Host packages

<Tabs>
  <Tab title="macOS (Homebrew)">

```bash
brew install icu4c openssl@3 libpq

export LIBRARY_PATH="$(brew --prefix openssl@3)/lib:${LIBRARY_PATH:-}"
export PKG_CONFIG_PATH="$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix icu4c)/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
export PATH="$(brew --prefix libpq)/bin:$PATH"
```

OpenSSL and OpenLDAP FFI `build.rs` units also probe `brew --prefix openssl@3` / `openldap` when `OPENSSL_*` / search paths are unset. PAM uses the macOS SDK `libpam`.

  </Tab>
  <Tab title="Debian / Ubuntu">

```bash
sudo apt-get update
sudo apt-get install -y build-essential pkg-config \
  libicu-dev libssl-dev libldap2-dev libpam0g-dev \
  postgresql-client-18
```

The Docker `rustbuild` stage installs the same core set: `build-essential`, `pkg-config`, `libicu-dev`, `libldap2-dev`, `libpam0g-dev`, `libssl-dev` on `rust:1-bookworm`.

  </Tab>
</Tabs>

## Build-time path bake-in

`crates/backend/utils/init/miscinit/src/boot_paths.rs` (and datetime path helpers) resolve installation directories with `option_env!("PGRUST_…")`, falling back to PostgreSQL’s documented `/usr/local/pgsql/…` layout when unset.

| Env var | Default if unset at compile time |
| --- | --- |
| `PGRUST_PGSHAREDIR` | `/usr/local/pgsql/share` |
| `PGRUST_PGBINDIR` | `/usr/local/pgsql/bin` |
| `PGRUST_PKGLIBDIR` | `/usr/local/pgsql/lib` |
| `PGRUST_SYSCONFDIR` | `/usr/local/pgsql/etc` |
| `PGRUST_INCLUDEDIR` | `/usr/local/pgsql/include` |
| `PGRUST_PKGINCLUDEDIR` | `/usr/local/pgsql/include` |
| `PGRUST_INCLUDEDIRSERVER` | `/usr/local/pgsql/include/server` |
| `PGRUST_LIBDIR` | `/usr/local/pgsql/lib` |
| `PGRUST_LOCALEDIR` | `/usr/local/pgsql/share/locale` |
| `PGRUST_DOCDIR` | `/usr/local/pgsql/share/doc` |
| `PGRUST_HTMLDIR` | `/usr/local/pgsql/share/doc` |
| `PGRUST_MANDIR` | `/usr/local/pgsql/share/man` |

`.cargo/config.toml` supplies workspace defaults when the process environment does not set them:

| Key | Value | Behavior |
| --- | --- | --- |
| `PGRUST_PGSHAREDIR` | `/tmp/pgrust_share` | Applied only if not already set (`force` is false). |
| `CARGO_INCREMENTAL` | `"0"` | Incremental compilation off for this workspace. |

<Warning>
`PGRUST_PGSHAREDIR` is compile-time. A wrong bake-in points timezone / timezonesets (and related share resolution) at the wrong tree and breaks boot. Prefer an explicit absolute path to a real share directory at build time. Runtime `-L` still supplies the share path for `--initdb` catalog files; tz layout follows the baked share dir.
</Warning>

Recommended local bake-in (repo checkout):

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
cargo build --release --locked --bin postgres
```

Docker `rustbuild` sets `ENV PGRUST_PGSHAREDIR=${PG_SHAREDIR}` with `PG_SHAREDIR=/usr/share/postgresql/18` so the image binary matches the share tree copied into the final stage.

### Vendored share tree

`vendor/postgres-18.3/share` includes the files `--initdb` / `--boot` and runtime path helpers expect, including:

- `postgres.bki`, `system_constraints.sql`, `system_functions.sql`, `system_views.sql`
- `information_schema.sql`, `sql_features.txt`, `snowball_create.sql`
- config samples (`postgresql.conf.sample`, `pg_hba.conf.sample`, `pg_ident.conf.sample`, …)
- `timezone/`, `timezonesets/`, `tsearch_data/`, `extension/`

## Release build

The only workspace `[[bin]]` named `postgres` lives on package `init` (`crates/_support/seam/init`). That package aggregates seam registration for ported backend and contrib crates. On non-wasm targets it also depends on the pgrust `initdb` driver; on wasm that dependency is omitted.

### Command

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
cargo build --release --locked --bin postgres
```

| Flag / input | Role |
| --- | --- |
| `--release` | Uses `[profile.release]`: `debug = 0`, `strip = "symbols"`, `lto = false`, `codegen-units = 1`. |
| `--locked` | Refuse to update `Cargo.lock`; matches Dockerfile and README. |
| `--bin postgres` | Builds the server entry shell only, not every workspace member as a binary. |
| `PGRUST_PGSHAREDIR` | Optional override of `.cargo/config.toml` default at compile time. |

Equivalent Docker stage command (with `CARGO_TARGET_DIR=/build/target`):

```bash
cargo build --release --locked --bin postgres
# then: cp /build/target/release/postgres /opt/pgrust-postgres
```

### Output location

| Build | Binary path |
| --- | --- |
| Default native release | `target/release/postgres` |
| Custom `CARGO_TARGET_DIR` | `$CARGO_TARGET_DIR/release/postgres` |
| Docker intermediate | staged as `/opt/pgrust-postgres`, installed as `/usr/local/bin/pgrust-postgres` (symlink/contract via entrypoint) |

Verify the artifact exists and is executable:

```bash
ls -l target/release/postgres
target/release/postgres --version
```

### Optional: package-scoped build

Smoke and harness comments also use:

```bash
PGRUST_PGSHAREDIR=/tmp/pgrust_share cargo build -p init --bin postgres
# older comments may say -p seams-init; the Cargo package name is `init`
```

## Post-build smoke (native)

After a successful release link, initialize and start with the same share path and the required runtime stack / IO settings:

```bash
target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres

ulimit -s 65520

RUST_MIN_STACK=33554432 target/release/postgres \
  -D /tmp/pgrust-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000
```

Connect with a Postgres 18 client:

```bash
psql -h /tmp -p 5432 -U postgres -d postgres \
  -c "select version(), 1 + 1 as two"
```

## wasm64 single-user notes

The tree supports a `wasm64-unknown-unknown` single-user build for browser / harness bring-up. This is not the native multi-process postmaster path.

### Target linker config

`.cargo/config.toml`:

```toml
[target.wasm64-unknown-unknown]
rustflags = ["-C", "link-arg=--allow-undefined"]
```

Native crates still declare OpenSSL / libxml2 / locale `extern "C"` symbols. Single-user `--single` does not call those paths, but the linker must resolve them: `--allow-undefined` turns unresolved C symbols into wasm imports. Hosts fault only if those imports are actually invoked.

### `wasm-libc-shim`

Package `wasm-libc-shim` is a shared libc stand-in for `target_family = "wasm"`. Consumers shadow `libc` with:

```rust
#[cfg(target_family = "wasm")]
use wasm_libc_shim as libc;
```

On native targets the crate is empty. On wasm it supplies Linux-shaped constants/types, allocator-backed `malloc`/`free`, and ENOSYS / no-op stubs for fork, SysV IPC, sockets, signals, and raw FS ops that single-user never requires on the boot path.

### Package differences on wasm

From `crates/_support/seam/init/Cargo.toml`:

- **Not linked:** `initdb` (subprocess bootstrap driver) — `cfg(not(target_family = "wasm"))`.
- **Linked:** `wasm-libc-shim`, and `getrandom` with `features = ["custom"]` (pgcrypto → rand path). The `postgres` bin registers a wasm-only xorshift custom backend so the module links without OS entropy.

Wasm `main` also installs a panic hook that writes to host stderr (std stderr is a no-op on this target) and obtains argv from a host `pgvfs` import when `std::env::args()` is empty.

### Profiles

| Profile | Purpose | Key settings | Build sketch |
| --- | --- | --- | --- |
| `wasm-boot` | Single-user boot bring-up | `opt-level = "z"`, `debug = "line-tables-only"`, `strip = false`, `panic = "abort"`, `codegen-units = 16` | `cargo +nightly build -Zbuild-std=std,panic_unwind,panic_abort --bin postgres --target wasm64-unknown-unknown --profile wasm-boot` |
| `wasm-prod` | Deployed browser module (e.g. pgrust.com) | `opt-level = "s"`, `lto = true`, `strip = true`, `codegen-units = 1`, `panic = "abort"` | `cargo +nightly build -Zbuild-std=std,panic_abort --bin postgres --target wasm64-unknown-unknown --profile wasm-prod` then `wasm-opt -O2` |

<Note>
The wasm64 target hard-codes `panic=abort`. SQL errors that rely on `catch_unwind` recovery can abort the process; error-free statements are the supported single-user path for bring-up.
</Note>

FFI `build.rs` units for OpenSSL and libxml2 intentionally emit **no** native link lines when `CARGO_CFG_TARGET_FAMILY=wasm`.

## Cargo workspace build knobs (adjacent)

These affect day-to-day source builds but are not required for a single release binary:

| Surface | Role |
| --- | --- |
| `cargo fast-check` | Alias for `cargo check --profile fast-check` (lighter than full release link). |
| `[profile.dev]` | `debug = "line-tables-only"`, `incremental = false` (linking dominates cost). |
| `scripts/check-crate`, `scripts/gate-crate` | Per-crate validation; see workspace workflow docs. |

## Failure modes at build time

| Symptom | Likely cause |
| --- | --- |
| Unresolved `ssl` / `crypto` / ICU / LDAP / PAM | Missing host packages or macOS `LIBRARY_PATH` / `PKG_CONFIG_PATH` not pointing at Homebrew kegs. |
| Lockfile mismatch with `--locked` | Local tree out of sync with committed `Cargo.lock`; resolve intentionally, do not drop `--locked` for release parity. |
| Server boots with wrong / missing timezone data | `PGRUST_PGSHAREDIR` baked to a path that does not contain `timezone` / `timezonesets`. Rebuild with the correct absolute share path. |
| `cargo build --bin postgres` ambiguity | Should not occur: only package `init` defines `[[bin]] name = "postgres"`. Use `-p init` if multiple bins confuse tooling. |
| wasm link fails on native C libs | Ensure `--target wasm64-unknown-unknown` and the target `rustflags` in `.cargo/config.toml` are active. |
| wasm `getrandom` link error | Custom backend is registered only in the `postgres` bin; build that bin, not intermediate crates that pull `getrandom` without the registration. |

## Next

<CardGroup>
  <Card title="Installation" href="/installation">
    Prerequisites, system packages, share-dir wiring, and producing the release binary from a clone.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    PGRUST_PGSHAREDIR, runtime `-L`, vendored share contents, and tz path failures.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    Build, `--initdb` with `-L`, start with stack and `io_method` settings, connect with psql.
  </Card>
  <Card title="Docker" href="/docker">
    Image build and official postgres-shaped run contract.
  </Card>
  <Card title="Workspace and crate checks" href="/workspace-workflow">
    fast-check profile, `scripts/check-crate`, and per-crate validation.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Runtime GUCs and build-time `PGRUST_*` path env vars.
  </Card>
</CardGroup>

---

## 20. Workspace and crate checks

> Cargo workspace scale, fast-check profile, scripts/check-crate and scripts/gate-crate, and how to validate a single ported crate plus seams-init.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/20-workspace-and-crate-checks.md
- Generated: 2026-07-10T05:33:12.202Z

### Source Files

- `Cargo.toml`
- `scripts/check-crate`
- `scripts/gate-crate`
- `scripts/cargo-fast-check`
- `.cargo/config.toml`
- `crates/_support/seam/init/src/lib.rs`

---
title: "Workspace and crate checks"
description: "Cargo workspace scale, fast-check profile, scripts/check-crate and scripts/gate-crate, and how to validate a single ported crate plus seams-init."
---

pgrust is a single Cargo workspace of **1466** members (`resolver = "2"`) under `crates/`. Day-to-day validation of a ported package uses `scripts/check-crate` and `scripts/gate-crate` with the `fast-check` profile so typecheck stays cheaper than a full workspace link. Cross-crate seam wiring is aggregated in the package colloquially called **seams-init** (`[package] name = "init"` at `crates/_support/seam/init`), which owns `init_all()`, the `postgres` binary entry shell, and the wiring/recurrence tests.

## Workspace scale and layout

| Fact | Value |
| --- | --- |
| Workspace members | 1466 (`Cargo.toml` `[workspace].members`) |
| Resolver | `"2"` |
| Default edition / publish | `edition = "2021"`, `publish = false` (`[workspace.package]`) |
| Package-name surface | Cargo `-p` selects **package** names (e.g. `pgcrypto`, `init`), not path segments |

Top-level trees under `crates/`:

| Tree | Role (approx. package count) |
| --- | --- |
| `backend/` | Core server port (~1248) |
| `_support/` | Shared types, seam core, init aggregator, todo guard (~125) |
| `common/` | Shared portability / utilities (~46) |
| `port/` | Port layer (~18) |
| `contrib/` | Ported contrib modules (~17) |
| `pl/` | PL/pgSQL-related crates (~8) |
| `interfaces/` | libpq-related (~3) |
| `wasm-libc-shim/` | Wasm link shim (1) |

:::files
crates/
  _support/
    seam/
      init/          # package name: init  (docs/scripts: "seams-init")
      seam_core/
    todo_guard/      # package name: todo_guard
    types/ ...
  backend/ ...
  common/ ...
  contrib/ ...
  interfaces/ ...
  pl/ ...
  port/ ...
  wasm-libc-shim/
scripts/
  cargo-fast-check
  check-crate
  gate-crate
.cargo/
  config.toml        # alias fast-check; default env
:::

About **600** packages are `*_seams` companions (underscore suffix, nested under the owner area). They are separate workspace members with package names like `detoast_seams`, not top-level `crates/<name>-seams` directories.

## Why full-workspace cargo is expensive

Root `Cargo.toml` notes that **linking dominates build time**. Profiles deliberately lighten debuginfo and turn off incremental compilation:

| Profile | Key settings |
| --- | --- |
| `dev` | `debug = "line-tables-only"`, `split-debuginfo = "unpacked"`, `incremental = false` |
| `dev.build-override` | `debug = 0`, `codegen-units = 256` |
| **`fast-check`** | inherits `dev`; `debug = 0`, `split-debuginfo = "off"`, `incremental = false` |
| `release` | `debug = 0`, `strip = "symbols"`, `lto = false`, `codegen-units = 1` |

`.cargo/config.toml` also forces `CARGO_INCREMENTAL = "0"` for every build unless already set in the environment.

Prefer **package-scoped** checks (`-p <package>`) over `cargo check --workspace` when iterating on one crate.

## Fast-check profile and wrappers

### Profile definition

```toml
# Cargo.toml
[profile.fast-check]
inherits = "dev"
debug = 0
split-debuginfo = "off"
incremental = false
```

### Cargo alias

```toml
# .cargo/config.toml
[alias]
fast-check = "check --profile fast-check"
```

Equivalent invocations:

```bash
cargo fast-check -p pgcrypto
cargo check --profile fast-check -p pgcrypto
scripts/cargo-fast-check -p pgcrypto
```

`scripts/cargo-fast-check` `cd`s to the repo root and runs:

```sh
exec cargo check --profile fast-check "$@"
```

### Default path env for builds

`.cargo/config.toml` sets (only if unset in the process environment):

| Variable | Default | Purpose |
| --- | --- | --- |
| `CARGO_INCREMENTAL` | `0` | Matches non-incremental workspace policy |
| `PGRUST_PGSHAREDIR` | `/tmp/pgrust_share` | Bake-in for share/tz `option_env!` lookups |

Override when needed, for example:

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" cargo build --release --locked --bin postgres
```

## `scripts/check-crate`

Typecheck one Cargo package under `fast-check`, then optionally a sibling seams package.

### Usage

```text
scripts/check-crate <crate> [cargo-check-args...]
```

Exit code **2** if `<crate>` is missing.

### Behavior

1. Resolve repo root from the script path and `cd` there.
2. Run:
   ```bash
   cargo check --profile fast-check -p "$crate" "$@"
   ```
3. If directory `crates/$crate-seams` exists (hyphen before `seams`), also run:
   ```bash
   cargo check --profile fast-check -p "$crate-seams" "$@"
   ```

### Constraints

| Input | Meaning |
| --- | --- |
| `<crate>` | Cargo **package** name passed to `-p` (e.g. `pgcrypto`, `detoast`, `init`) |
| Extra args | Forwarded to both `cargo check` invocations |

<Warning>
The optional companion check looks for a **top-level** directory `crates/<package>-seams` and package name `<package>-seams`. The live tree almost exclusively uses nested `*_seams` packages (underscore). For those, pass the seams package explicitly if you need it:

```bash
scripts/check-crate detoast
cargo check --profile fast-check -p detoast_seams
```
</Warning>

## `scripts/gate-crate`

Local gate for one package: typecheck + unit tests, with an optional wider gate.

### Usage

```text
scripts/gate-crate [--full] <crate>
```

Exit code **2** if the package argument is missing.

### Pipeline

```mermaid
flowchart TD
  A["scripts/gate-crate [--full] PACKAGE"] --> B["scripts/check-crate PACKAGE"]
  B --> C["cargo check --profile fast-check -p PACKAGE"]
  B --> D{"crates/PACKAGE-seams exists?"}
  D -->|yes| E["cargo check --profile fast-check -p PACKAGE-seams"]
  D -->|no| F["skip companion"]
  C --> G["cargo test -p PACKAGE"]
  E --> G
  F --> G
  G --> H{"--full?"}
  H -->|no| I["done"]
  H -->|yes| J["cargo test -p seams-init"]
  J --> K["cargo test -p no-todo-guard"]
```

| Step | Command | Intent |
| --- | --- | --- |
| 1 | `scripts/check-crate "$crate"` | Fast typecheck (+ optional hyphen seams companion) |
| 2 | `cargo test -p "$crate"` | Package unit/integration tests |
| 3 (only `--full`) | `cargo test -p seams-init` | Aggregator / wiring tests (see package-name note) |
| 4 (only `--full`) | `cargo test -p no-todo-guard` | Workspace `todo!()` scan tests (see package-name note) |

### Package-name mapping for `--full`

Comments and scripts refer to historical labels **seams-init** and **no-todo-guard**. Cargo metadata package names are:

| Colloquial / script `-p` | Actual `[package] name` | Path |
| --- | --- | --- |
| `seams-init` | `init` | `crates/_support/seam/init` |
| `no-todo-guard` | `todo_guard` | `crates/_support/todo_guard` |

`scripts/gate-crate --full` currently passes the colloquial names into Cargo. Prefer the real package names when invoking Cargo directly:

```bash
cargo test -p init
cargo test -p todo_guard
# strict todo scan (ignored until tree is clean):
cargo test -p todo_guard -- --ignored
```

## Validate a single ported crate

<Steps>
  <Step title="Identify the Cargo package name">
    Read `[package].name` in the crate’s `Cargo.toml` (often matches the directory leaf: `pgcrypto`, `citext`, `detoast`). Use that string for `-p` and the gate scripts—not the full path under `crates/`.
  </Step>
  <Step title="Typecheck with fast-check">
    ```bash
    scripts/check-crate pgcrypto
    # or
    cargo fast-check -p pgcrypto
    ```
    Success: `cargo check` exits 0 for the package under profile `fast-check`.
  </Step>
  <Step title="Run package tests">
    ```bash
    scripts/gate-crate pgcrypto
    # equivalent core of the gate:
    # scripts/check-crate pgcrypto && cargo test -p pgcrypto
    ```
  </Step>
  <Step title="If the crate installs seams, wire seams-init">
    Owner crates that install seams expose `pub fn init_seams()`. The aggregator must:
    1. Depend on the package in `crates/_support/seam/init/Cargo.toml`.
    2. Call `<lib>::init_seams();` once inside `init::init_all()` in `crates/_support/seam/init/src/lib.rs` (kept as one call per ported crate).

    `init_all()` contains no `set()` logic of its own—only ordered calls into owners.
  </Step>
  <Step title="Run aggregator tests">
    ```bash
    cargo test -p init
    ```
    Notable tests:
    - Unit module `recurrence_guard`: every crate that installs seams via `init_seams()` must be called from `init_all()` (catches merge silent-drop of wiring lines).
    - Integration `tests/no_double_install.rs`: drives a full `init_all()` once and fails on `"seam installed twice: ..."` double-`set()`.
    - Builtin completeness helpers under `builtin_gap_baseline` / related registry tests.
  </Step>
  <Step title="Optional full local gate">
    ```bash
    scripts/gate-crate --full pgcrypto
    ```
    After the package gate, runs the script’s full-step `cargo test` targets. Use `cargo test -p init` / `cargo test -p todo_guard` if the historical package labels fail to resolve.
  </Step>
</Steps>

### Expected signals

| Check | Healthy signal |
| --- | --- |
| `check-crate` / `cargo fast-check -p …` | Exit 0; no type/link errors for that package graph |
| `cargo test -p <crate>` | Exit 0; crate’s own tests pass |
| `cargo test -p init` | Exit 0; recurrence + no-double-install and related tests pass |
| `todo_guard` report | `report_todo_count` always runs; strict `no_todo_or_unimplemented_in_tree` is `#[ignore]` until the tree is clean |

## seams-init (`init`) role in the gate

### Runtime path

The `postgres` binary lives in the `init` package (`[[bin]] name = "postgres"`). Startup:

1. Create top memory context.
2. Call `init::init_all()` (every owner’s `init_seams()`).
3. Dispatch via `backend_main_main::pg_main` / `main_main::pg_main`.

That is the process-level reason package tests on `init` matter after wiring a new seam owner: boot installs seams only through this aggregator.

### Guard classes covered by `init` tests

| Guard | What it catches |
| --- | --- |
| Recurrence / wiring | Seam-installing crate missing from `init_all()` (compile-time invisible; fails the unit test) |
| No double install | Two owners `set()` the same seam slot in one `init_all()` pass |
| Builtin gap baseline | Live builtin registry gap held to a checked-in baseline set |
| Optional runtime smoke | `runtime_seam_miss_smoke` (ignored by default; needs a built `postgres` binary) |

Empty `init_seams()` functions exist on some crates solely so the recurrence/uniformity machinery stays consistent even when no `set()` is required.

## `todo_guard` (no-todo-guard)

Package `todo_guard` scans for real `todo!()` / `unimplemented!()` under crate sources (comments and string literals excluded).

| Test | Behavior |
| --- | --- |
| `report_todo_count` | Always on; prints count (use `-- --nocapture` for the list); does not fail |
| `no_todo_or_unimplemented_in_tree` | Strict gate; currently `#[ignore]` until offender count is zero |

```bash
cargo test -p todo_guard -- --nocapture
cargo test -p todo_guard -- --ignored
```

## Example commands

<CodeGroup>
```bash title="Single package typecheck"
scripts/check-crate hstore
cargo fast-check -p hstore
```

```bash title="Package gate"
scripts/gate-crate hstore
```

```bash title="Package + aggregator + todo_guard (script)"
scripts/gate-crate --full hstore
```

```bash title="Direct aggregator / guard packages"
cargo test -p init
cargo test -p todo_guard -- --nocapture
```

```bash title="Explicit seams companion (underscore)"
cargo check --profile fast-check -p detoast
cargo check --profile fast-check -p detoast_seams
```
</CodeGroup>

## Failure modes

| Symptom | Likely cause | What to do |
| --- | --- | --- |
| `package ID specification ... did not match any packages` | Wrong `-p` name (path vs package), or script `--full` colloquial names `seams-init` / `no-todo-guard` | Use `init` / `todo_guard` or the real `[package].name` |
| Companion seams never checked by `check-crate` | Expects `crates/<name>-seams`; tree uses nested `*_seams` | Check `-p <name>_seams` explicitly |
| Recurrence_guard panic listing unwired crates | New `init_seams()` + `::set(` without `init_all()` line / dep | Add path dep + `<lib>::init_seams();` in `init` |
| `"seam installed twice: ..."` in `no_double_install` | Two owners install the same seam | Remove duplicate install; keep single owner |
| Wrong tz/share at runtime after rebuild | Built without correct `PGRUST_PGSHAREDIR` | Override env or use intended share path; see install-path docs |
| Full workspace check thrashing disk/CPU | 1400+ members, linking-heavy | Stay on `scripts/check-crate` / `-p` scope |

## Related pages

<CardGroup>
  <Card title="Seams and builtin libraries" href="/seams-and-builtins">
    Seam registration model, seams-init aggregation, and dfmgr builtin registration.
  </Card>
  <Card title="Build from source" href="/build-from-source">
    Host packages, PGRUST_PGSHAREDIR bake-in, and `cargo build --release --locked --bin postgres`.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    Build-time path env vars, `-L` override, and share/tz failure modes.
  </Card>
  <Card title="Run regression tests" href="/regression-tests">
    Clone-only `scripts/run-regression` against vendored PG 18.3 oracles.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Runtime GUCs and build-time `PGRUST_*` path variables.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Stack, io_method, share path, and related operational failures.
  </Card>
</CardGroup>

---

## 21. Troubleshooting

> Known failure modes: missing io_method=sync, stack overflows, wrong share/tz path, initdb option gaps, root execution refused, missing psql client, and unported extension loads.

- Page Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/pages/21-troubleshooting.md
- Generated: 2026-07-10T05:34:33.966Z

### Source Files

- `README.md`
- `scripts/run-recovery-tap`
- `scripts/run-regression`
- `crates/backend/main/main_main/src/lib.rs`
- `crates/backend/storage/buffer/bufmgr/src/buf_aio.rs`
- `.cargo/config.toml`
- `docker/entrypoint.sh`

---
title: "Troubleshooting"
description: "Known failure modes: missing io_method=sync, stack overflows, wrong share/tz path, initdb option gaps, root execution refused, missing psql client, and unported extension loads."
---

pgrust's single `postgres` binary fails in a small set of high-frequency ways: GUC defaults that do not match operational expectations (`io_method`, `max_stack_depth`), process stack limits, baked-in share/timezone paths, a reduced `postgres --initdb` option surface, a hard root check, an external `psql` client dependency, and extension loads that never reach a real `dlopen` C ABI.

## Symptom index

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Postmaster panic / abort around AIO or `io_method = io_uring` | Default or selected async IO method | Start with `-c io_method=sync` |
| `stack depth limit exceeded`, seam-frame overflow, refuse to boot | OS stack / `RUST_MIN_STACK` / `max_stack_depth` too small | Raise `ulimit -s`, `RUST_MIN_STACK`, and `max_stack_depth` |
| Missing timezone / bootstrap templates / wrong share tree | Wrong `PGRUST_PGSHAREDIR` at build time, or missing `-L` at initdb | Bake share path at build; pass `-L` to `--initdb` |
| `unrecognized initdb option "…"` | Full C `initdb` flag not ported | Use only supported argv; space-separated form only |
| `"root" execution of the PostgreSQL server is not permitted` | Running as uid 0 | Run as unprivileged user (Docker steps down automatically) |
| `error: no psql client found` | Clone-only regression runner | Install PG 18 client or set `PGRUST_PSQL` |
| `could not load library "…": loading C-ABI extension libraries is not supported` | Unported / third-party `.so` extension | Use ported builtins only; see [Use contrib extensions](/contrib-extensions) |

## Missing `io_method=sync`

The GUC `io_method` is `PGC_POSTMASTER` with boot value `worker` (`DEFAULT_IO_METHOD = IOMETHOD_WORKER`), matching Postgres 18. Operational docs, Docker, and regression harnesses still **force** synchronous IO:

```bash
-c io_method=sync
```

| Surface | Behavior |
| --- | --- |
| README / local start | Explicit `-c io_method=sync` |
| `docker/entrypoint.sh` | Injects `PGRUST_SERVER_OPTS` including `io_method=sync` for temp and final servers |
| `scripts/run-regression` | Postmaster argv includes `-c io_method=sync` |
| `scripts/run-recovery-tap` | Writes `io_method = sync` into `$TEMP_CONFIG` for every TAP node |

Selecting `io_method = io_uring` panics at method resolution with a message that points back to `io_method = sync`. Prefer the documented sync setting for any local or harness boot unless you have verified another method end-to-end for your workload.

<Warning>
Starting without `-c io_method=sync` diverges from every first-party runner in this repository. Prefer the same flags the README and Docker entrypoint use.
</Warning>

### Verification

```bash
# In a connected session (GUC is postmaster-level; show only reflects boot setting)
psql -h /tmp -p 5432 -U postgres -d postgres -c "SHOW io_method;"
```

Expect `sync` when you started with the required override.

## Stack overflows

Rust frames, seam indirection, and panic-based `ereport(ERROR)` produce deeper stacks than C Postgres. Three independent knobs matter:

| Control | Typical value | Role |
| --- | --- | --- |
| `ulimit -s` | `65520` | Process stack size (kB-class units as set by the shell) |
| `RUST_MIN_STACK` | `33554432` (32 MiB) | Main/thread stack for Rust runtime |
| `max_stack_depth` GUC | boot `2048` kB (raised vs C's `100`); harness often `60000` | Postgres stack-depth check |

pgrust intentionally diverges from C on the `max_stack_depth` boot value (`2048` kB vs `100` kB) so catalog-heavy clients (for example `\d`) do not fail on a stock instance. Harnesses and Docker still raise it further (`60000` or `7000kB` in recovery TAP).

### Required start pattern

```bash
ulimit -s 65520
export RUST_MIN_STACK=33554432

target/release/postgres \
  -D /tmp/pgrust-data \
  -F \
  -c listen_addresses= \
  -k /tmp \
  -p 5432 \
  -c io_method=sync \
  -c max_stack_depth=60000
```

Docker applies the same stack limits and GUC pair automatically via `docker/entrypoint.sh`.

### Symptoms

- SQL error: `stack depth limit exceeded`
- Failures on deep catalog queries or `\d` when `max_stack_depth` is left near C defaults
- Hard process stack overflow if only GUCs are raised but `ulimit -s` / `RUST_MIN_STACK` stay low

## Wrong share / timezone path

Install directories are compile-time `option_env!` values in `boot_paths` (`PGRUST_PGSHAREDIR` and related `PGRUST_*` path vars). Defaults without env are `/usr/local/pgsql/share` (and siblings). Wrong bake-in is a recurring footgun: the postmaster then resolves `timezone/` and `timezonesets/` against a missing or unrelated tree.

| Knob | When | Effect |
| --- | --- | --- |
| `PGRUST_PGSHAREDIR` at **build** | `cargo build` | Baked into binary; used for share/tz derivation at runtime |
| `.cargo/config.toml` `[env]` | Default build | Sets `PGRUST_PGSHAREDIR = "/tmp/pgrust_share"` when unset (override with an explicit env value) |
| `-L <sharedir>` on **`--initdb`** | Cluster create | Share templates for bootstrap (`postgres.bki`, `system_*.sql`, samples, extension SQL) |
| Runtime path relative to executable | Postmaster | When binary is outside a normal prefix layout, relative derivation may miss the tree |

### Documented build + init

```bash
PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
  cargo build --release --locked --bin postgres

target/release/postgres --initdb \
  -D /tmp/pgrust-data \
  -L "$PWD/vendor/postgres-18.3/share" \
  --no-locale \
  --encoding UTF8 \
  -U postgres
```

Docker passes `-L "$PG_SHAREDIR"` (default `/usr/share/postgresql/18` in the image) because the binary lives under `/usr/local/bin`, outside the PGDG bindir layout.

### Vendored tree contents

`vendor/postgres-18.3/share` holds bootstrap and runtime data: `postgres.bki`, system SQL, conf samples, `timezone/`, `timezonesets/`, `tsearch_data/`, `extension/`. Initdb without a correct `-L` (or a binary baked to a tree you never populated) fails early in template setup or later when timezone abbreviation files are read.

## Initdb option gaps

`postgres --initdb` / `postgres initdb` is a pgrust driver inside the same binary, not full C `initdb`. Unsupported flags fail with:

```text
unrecognized initdb option "…"
```

### Supported argv (subset)

| Flag | Notes |
| --- | --- |
| `-D` / `--pgdata` | Required |
| `-U` / `--username` | Bootstrap superuser |
| `-L` | Share directory override |
| `-E` / `--encoding` | Only `SQLASCII`, `UTF8`/`UNICODE`, `LATIN1` |
| `--lc-collate`, `--lc-ctype`, `--locale`, `--no-locale` | Locale flags; `--locale` is accepted but only `C` is honored in practice |
| `--wal-segsize` | Segment size in MB |

### Gaps and parsing rules

- **Space-separated only**: `--opt value`, not `--opt=value` (Docker documents this for `POSTGRES_INITDB_ARGS`).
- **No** `--pwfile`: Docker applies the superuser password later with `ALTER ROLE` against a temp server.
- **No** `-A` / `--auth`, `--data-checksums`, and other C-only switches — they error as unrecognized.
- **No** `POSTGRES_INITDB_WALDIR` → pgrust `--waldir` mapping unless the entrypoint translates it; prefer supported flags.
- Non-empty existing `PGDATA` is refused: `directory "…" exists but is not empty`.

Success ends with:

```text
Success. You can now start the database server using pgrust/postgres -D <pgdata>
```

## Root execution refused

`check_root` in `main_main` refuses euid `0` before dispatch (including `initdb`):

```text
"root" execution of the PostgreSQL server is not permitted.
The server must be started under an unprivileged user ID to prevent
possible system security compromise.  …
```

Also refused: real uid ≠ effective uid (`{progname}: real and effective user IDs must match`).

### Exceptions (root allowed)

| First argv | Root check |
| --- | --- |
| `--help` / `-?`, `--version` / `-V` | Exit before check |
| `--describe-config` | Check skipped |
| `-C` as first switch (with following var) | Check skipped (pg_ctl-style) |

Docker may start as root to fix ownership, then steps down to `postgres` before running the server — matching the official image contract.

## Missing `psql` client

pgrust ships the **server** binary only. There is no pgrust `psql`. Clients and harnesses need a PostgreSQL 18 `psql` (and Docker images bundle one).

### Clone-only regression runner

`scripts/run-regression` resolves the client in order:

1. `PGRUST_PSQL`
2. `command -v psql`
3. `/tmp/pgrust_pginstall/bin/psql`

If none work:

```text
error: no psql client found.
…
  macOS:  brew install libpq && export PATH="$(brew --prefix libpq)/bin:$PATH"
  or:     export PGRUST_PSQL=/path/to/psql
```

### Install hints

| Platform | Package / path |
| --- | --- |
| macOS | `brew install libpq`; put `$(brew --prefix libpq)/bin` on `PATH` |
| Debian/Ubuntu | `postgresql-client-18` (see README) |
| Docker | Use in-container `psql` against the running service |

Recovery / isolation TAP scripts additionally require a full C install (`PG_INSTALL`) for tools beyond `psql` (`initdb`, `pg_ctl`, …) — a different failure class than clone-only regression.

## Unported extension loads

dfmgr resolves libraries by simple basename (`$libdir/foo.so` → `foo`) against an **in-process builtin registry** first. Ported modules register via `register_builtin_library`. Anything not registered falls through to the OS loader edge, which **does not** `dlopen` real C-ABI `.so` files in this backend.

### Errors you will see

| Message | Meaning |
| --- | --- |
| `could not load library "…": loading C-ABI extension libraries is not supported by this server` | Unported or third-party shared library path |
| `could not access file "…": %m` | Path/stat failure before load |
| `could not find function "…" in file "…"` | Builtin library registered but symbol missing |
| `incompatible library "…": missing magic block` | Magic-block path (OS open path); not the normal unported case |

### Ported as builtins (representative)

Registered contrib / language modules include: `citext`, `hstore`, `ltree`, `pg_trgm`, `pgcrypto`, `pg_stat_statements`, `uuid-ossp`, `pg_prewarm`, `injection_points`, `plpgsql`, `regress` (test helpers), snowball, and output plugin `test_decoding`. SQL/control files still come from the share tree (`CREATE EXTENSION` against vendored `extension/`).

### Not generally supported

- Arbitrary Postgres extensions built as `.so` against C Postgres
- PL/Python, PL/Perl, PL/Tcl (status: not generally compatible)
- Unported contrib modules not registered as builtins

`LOAD` of a ported name (for example `plpgsql`) runs the in-process `_PG_init` equivalent without touching the OS loader.

## Decision flow

```text
Boot / connect failure?
├─ root / setuid? ────────────────────► run as unprivileged user
├─ no -c io_method=sync? ─────────────► add io_method=sync
├─ stack errors / refuse to boot? ────► ulimit + RUST_MIN_STACK + max_stack_depth
├─ missing bki / timezone / templates? ► rebuild with PGRUST_PGSHAREDIR; initdb -L
├─ unrecognized initdb option? ───────► drop unsupported C flags; space-separated only
├─ regression: no psql? ──────────────► install client / PGRUST_PSQL
└─ CREATE EXTENSION / LOAD fails? ────► only ported builtins; no C-ABI .so
```

## Quick recovery checklist

<Steps>
  <Step title="Confirm binary and share bake-in">
    Rebuild with an explicit share path when the default `/tmp/pgrust_share` is empty:

    ```bash
    PGRUST_PGSHAREDIR="$PWD/vendor/postgres-18.3/share" \
      cargo build --release --locked --bin postgres
    ```
  </Step>
  <Step title="Recreate PGDATA with -L">
    ```bash
    target/release/postgres --initdb \
      -D /tmp/pgrust-data \
      -L "$PWD/vendor/postgres-18.3/share" \
      --no-locale --encoding UTF8 -U postgres
    ```
  </Step>
  <Step title="Start with required stack and IO settings">
    Use the `ulimit` / `RUST_MIN_STACK` / `io_method=sync` / `max_stack_depth` pattern above (or Docker, which injects them).
  </Step>
  <Step title="Connect and smoke-test">
    ```bash
    psql -h /tmp -p 5432 -U postgres -d postgres \
      -c "select version(), 1 + 1 as two"
    ```
  </Step>
</Steps>

## Related pages

<CardGroup>
  <Card title="Run the server" href="/run-server">
    Postmaster flags, mandatory IO/stack settings, and readiness checks.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `io_method`, `max_stack_depth`, `RUST_MIN_STACK`, `ulimit`, and `PGRUST_*` path env vars.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    Build-time share wiring, runtime `-L`, and timezone tree layout.
  </Card>
  <Card title="initdb options reference" href="/initdb-reference">
    Supported initdb argv and unsupported flags that error.
  </Card>
  <Card title="Use contrib extensions" href="/contrib-extensions">
    Ported builtin modules versus unported extension loads.
  </Card>
  <Card title="Run regression tests" href="/regression-tests">
    `scripts/run-regression`, `PGRUST_PSQL`, and clone-only client requirements.
  </Card>
  <Card title="Docker" href="/docker">
    Entrypoint-injected server opts and first-boot init behavior.
  </Card>
  <Card title="Compatibility and status" href="/compatibility">
    Non-production status and extension/PL boundaries.
  </Card>
</CardGroup>

---
