Home / Alt manpages / initdb(1)

  • initdb(1)
  • User command
  • linux

Create a PostgreSQL 16 Cluster Safely with initdb

Get initdb wrong once and you either end up with a trust-authenticated cluster anyone on the box can read, or a directory you cannot cleanly remove. You will finish with a new PostgreSQL database cluster in a directory you choose, with its owner, authentication rules, locale and durability settings understood. The examples match PostgreSQL 16.15, the version installed here, and use the Debian or Ubuntu binary at /usr/lib/postgresql/16/bin/initdb.

Allow about fifteen minutes. You need PostgreSQL server binaries and a shell. Run initdb as the operating-system user that will run PostgreSQL, never as root. The command creates persistent database files, so use a fresh directory and read the removal warning before experimenting.

1. Check the installed command

First confirm the binary and version. These are ordinary read-only commands and need no elevated privileges:

$ INITDB=/usr/lib/postgresql/16/bin/initdb
$ "$INITDB" --version
initdb (PostgreSQL) 16.15 (Ubuntu 16.15-0ubuntu0.24.04.1)
$ "$INITDB" --help | sed -n '1,12p'
Usage:
  initdb [OPTION]... [DATADIR]

Options:
  -A, --auth=METHOD         default authentication method for local connections
  -D, --pgdata=DIR          location for this database cluster
  -E, --encoding=ENCODING   set default encoding
  -k, --data-checksums      use data page checksums
  -U, --username=NAME       database superuser name

The installed manpage documents the same release as PostgreSQL 16.15. Some systems put initdb on PATH; using the absolute path here stops a different PostgreSQL major version being picked up by accident.

Checkpoint

Stop if the version is not the one you planned to use. A cluster is tied to its major PostgreSQL version and is not a substitute for an upgrade procedure.

2. Choose and prepare an empty data directory

Pick a directory with enough space and a stable backup plan. In a real installation, use a service-specific path such as /var/lib/postgresql/16/main, following the packaging instructions for that host. This guide uses a placeholder so it cannot overwrite a distribution-managed cluster:

$ DATA_DIR=/srv/postgresql/16/example
$ test ! -e "$DATA_DIR" || { echo "refusing to use an existing path"; exit 1; }
$ mkdir -p "$DATA_DIR"
$ chmod 700 "$DATA_DIR"

If the parent directory is root-owned, an administrator can create the empty directory and assign it to the database account. The initialisation itself must then run as that account, for example:

# install -d -o postgres -g postgres -m 700 /srv/postgresql/16/example
$ sudo -u postgres /usr/lib/postgresql/16/bin/initdb -D /srv/postgresql/16/example

The # prompt marks the one privileged preparation command. Do not prefix the actual initdb invocation with sudo unless it switches to the database owner. Running it as root is refused, because the server must later read and write the files as that same user.

3. Decide authentication before creating the cluster

initdb writes initial rules to pg_hba.conf. The convenient default is trust, which lets matching local users connect without a password. That is unsafe on a shared machine. Select methods explicitly for the connections you expect:

$ "$INITDB" -D "$DATA_DIR" \
    --auth-local=peer \
    --auth-host=scram-sha-256 \
    --username=appadmin \
    --pwprompt

You will be prompted for the bootstrap superuser password. It is not an argument, so it never lands in shell history or the process list. If automation needs a password file, --pwfile=FILENAME reads its first line; protect that file from other users and remove it after use.

Warning

Do not use trust merely to make a connection test easy. It is the setting most likely to bite you later, on a machine you forgot was still using it.

4. Set locale, encoding and checksums deliberately

Skip the locale options and the cluster inherits locale settings from the environment. PostgreSQL 16 uses the libc locale provider by default. Those choices affect sorting, character classification and the default encoding of databases created from the templates.

For a predictable English-language test cluster, an explicit configuration could be:

$ "$INITDB" -D "$DATA_DIR" \
    --locale=en_GB.UTF-8 \
    --encoding=UTF8 \
    --locale-provider=libc \
    --data-checksums

Do not combine this with step 3: it is an alternative invocation for a new directory, not an addition to it. The locale must exist on the host, so check with locale -a first and read the locale summary initdb prints. Checksums help detect certain storage corruption but add overhead; enabling them is an initialisation-time choice, and there is no config edit that switches them on later.

For a cluster that needs ICU collation, use --locale-provider=icu and an appropriate --icu-locale, but confirm the server was built with ICU support first. Mixing per-category locale options can produce surprising behaviour, so stick to one coherent locale unless you have a specific reason to split them.

5. Verify the result

Successful output ends with instructions for starting the server. Check the directory and the generated authentication file as the cluster owner:

$ test -f "$DATA_DIR/PG_VERSION" && cat "$DATA_DIR/PG_VERSION"
16
$ stat -c '%U %a %n' "$DATA_DIR" "$DATA_DIR/pg_hba.conf"
postgres 700 /srv/postgresql/16/example
postgres 600 /srv/postgresql/16/example/pg_hba.conf
$ grep -E '^(local|host)[[:space:]]' "$DATA_DIR/pg_hba.conf"

Your exact owner and authentication lines will differ. What you are checking is that PG_VERSION says 16, the data directory belongs to the server user, and pg_hba.conf contains the methods you chose. If the locale summary is wrong, stop before starting the server and recreate the cluster with corrected options.

6. Recover from a failed or unwanted initialisation

By default, initdb removes the files it created when it cannot complete. The --no-clean option keeps partial files for debugging and is not appropriate for a normal retry. A failed directory is not a usable cluster.

If this example created a disposable cluster and you are certain the path is correct, remove only that directory:

$ case "$DATA_DIR" in
    /srv/postgresql/16/example) rm -rf -- "$DATA_DIR" ;;
    *) echo "refusing unexpected path: $DATA_DIR"; exit 1 ;;
  esac

Recovery

This is irreversible. Never adapt the command to a production path without checking it with printf '%s\n' "$DATA_DIR" first. Recreating a cluster does not recover databases or configuration from the removed files; restore from backups instead.

Done means

  • Version confirmed. The installed initdb reports PostgreSQL 16.15, or the version you intentionally selected.
  • Correct owner. The cluster was initialised by its server owner, not root.
  • Directory checked. PG_VERSION contains 16 and the directory permissions are private.
  • Authentication chosen. pg_hba.conf uses an authentication method you chose, not an unexamined default.
  • Settings deliberate. Locale, encoding, checksums and WAL layout were treated as creation-time decisions.