# Installation

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

- 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

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