Create a PostgreSQL 16 cluster safely with initdb
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.
The route
Jump straight to the step you need, or tick off Done means at the end.
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 do not need 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 prevents a different PostgreSQL major version from being selected 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 may create the empty directory and assign it to the database account. The initialisation itself must then be 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 changes to the database owner. Running it as root is refused because the server must later be able to read and write the files as the 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. The password is not an argument, so it does not appear in shell history or the process list. If automation requires a password file, --pwfile=FILENAME reads its first line; protect that file from other users and remove it after use. Do not use trust merely to make a connection test easy.
4. Set locale, encoding and checksums deliberately
If you omit locale options, 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 second command with step 3: it is an alternative invocation for a new directory. The locale must exist on the host. Check with locale -a first and read the locale summary printed by initdb. Checksums help detect certain storage corruption but can add overhead; enabling them is an initialisation-time choice and cannot be switched on later with a simple configuration edit.
For a cluster that needs ICU collation, use --locale-provider=icu and an appropriate --icu-locale, but confirm that the server was built with ICU support. Inconsistent per-category locale options can produce surprising behaviour, so prefer 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"
The exact owner and authentication lines depend on your account and options. The useful checks are that PG_VERSION says 16, the data directory belongs to the server user, and the generated pg_hba.conf contains the methods you chose. If the locale summary is wrong, stop before starting the server and recreate the fresh cluster with corrected options.
6. Recover from a failed or unwanted initialisation
By default, initdb removes 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 should not be treated as 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
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
- The installed
initdbreports PostgreSQL 16.15, or the version you intentionally selected. - The cluster was initialised by its server owner, not root.
PG_VERSIONcontains16and the directory permissions are private.pg_hba.confuses an authentication method you chose, not an unexamined default.- Locale, encoding, checksums and WAL layout were treated as creation-time decisions.