Reusing an existing cluster

Version 0.6.3 Updated Sep 30, 2026

When the data directory already holds a cluster (its PG_VERSION marker is present) and PG_PASSWORD is not set, the bootstrap reads the superuser password back from the install tree's password file (<install>/.pgpass, which initdb was given) instead of generating a fresh one, so connections to the reused cluster succeed. If that file is missing, unreadable, or empty the bootstrap fails and names the data directory and the file: set PG_PASSWORD to the password that initialized the cluster, or remove the stale cluster. An explicit PG_PASSWORD always wins.

An explicit password short-circuits the whole check: the password file is not read at all, so setting PG_PASSWORD is a working escape from a cluster whose stored file has been lost.

When the data directory holds no cluster, a password file left in the install tree by an earlier, reaped cluster is removed before the new cluster is initialized. In releases after 0.6.0 this applies whether or not PG_PASSWORD is set. initdb reads that file, so a stale one used to give the fresh cluster the old password while the bootstrap reported a new one, and every process after the first in a persistent install root (PG_EMBED_ROOT, or the default per-user root) failed with password authentication failed. Under cargo nextest, where each test is its own process, that was every test but the first. A stale path that cannot be removed fails the bootstrap with ClusterPasswordUnreadable rather than letting initdb read it.

With an explicit PG_DATA_DIR, the password file belongs to the install tree, not to a data directory, so an install tree serves one such cluster at a time. Point two clusters at one PG_RUNTIME_DIR with different PG_DATA_DIR values and a fresh cluster in the second data directory removes the file the first cluster's reuse depends on. Before this change, the fresh cluster was instead initialized with the first cluster's password, and nobody could log in to it. Give each cluster its own install root, for example its own PG_EMBED_ROOT, or set PG_PASSWORD for both.

A failed connection to the admin database keeps the driver's error in the chain, and the message names its cause, for example failed to connect to admin database: db error: FATAL: password authentication failed for user "postgres" rather than a bare db error.

The file itself must be a regular file of no more than 4 KiB. The bootstrap opens it once and takes both its type and its size from that one handle, so a path replaced after a check cannot redirect the read. A FIFO, a directory, or any other non-regular file is refused rather than waited on: the Unix open carries O_NONBLOCK, so a FIFO with no writer returns at once and is then rejected for its type. A file longer than the cap is refused without being read. Both refusals return ClusterPasswordUnreadable, the error an unreadable file returns.

Probing the data directory fails loudly as well. Only a missing PG_VERSION marker means there is no cluster; a permission error, or any other failure to read that marker, returns ClusterPasswordUnreadable rather than reporting no cluster, so a data directory that is temporarily unsearchable cannot be mistaken for a fresh one and started with a new password.

Three items at the crate root expose the same logic to a consumer that manages its own Settings.

Table: Password-reuse API.

Item Purpose
stored_cluster_password Query. Given the data directory and the password file, returns Ok(None) when the data directory holds no cluster, Ok(Some(password)) when it does, and an error when the data directory cannot be probed or when the file is missing, unreadable, empty, not a regular file, or larger than 4 KiB.
reuse_existing_password Command. Takes the same two paths plus the mutable Settings and whether the caller supplied a password, aligns settings.password with the cluster on disk, and returns the outcome.
PasswordReuseOutcome The bounded outcome of a successful call: Reused, ExplicitPassword, or NoCluster. Each is also an outcome label of the password_reuse tracing event, which carries four further labels for the failures.

reuse_existing_password emits a warning-level password_reuse event on every failure branch before the error is returned, labelled probe_failed, missing_file, unreadable_file, or empty_file. A failure returns an error rather than a PasswordReuseOutcome, so those four labels appear only in the event: the field accepts all seven values, the returned enum carries the three successes. No password is ever a label. stored_cluster_password emits nothing at all: it is a query, so a caller that wants the event calls the command.

The same call records one Metric::PasswordReuse count, once per call and never from the query. The crate takes no metrics dependency, because a library should not choose one on a consumer's behalf; install a MetricsRecorder and forward each count to whatever you already run.

use std::sync::Arc;

use pg_embedded_setup_unpriv::observability::{
    Metric,
    MetricsRecorder,
    install_metrics_recorder,
};

struct Forwarding;

impl MetricsRecorder for Forwarding {
    fn record(&self, metric: Metric) {
        let Metric::PasswordReuse(outcome) = metric;
        // `outcome` is an enum, so it is safe to use as a label directly.
        println!("password_reuse {outcome:?}");
    }
}

let _guard = install_metrics_recorder(Arc::new(Forwarding));

The outcome is an enum rather than a string, so the label set is bounded by construction: a password or a path cannot reach a metric through it. The seven values are Reused, ExplicitPassword and NoCluster for the success branches, and ProbeFailed, MissingFile, UnreadableFile and EmptyFile for the failures. ProbeFailed and UnreadableFile stay distinct even though both return ClusterPasswordUnreadable. With no recorder installed, recording costs a branch and a return.