# Initialize a cluster

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

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

## Source Files

- `crates/backend/main/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>
