Home / Alt manpages / initdb(1)

  • initdb(1)
  • User command
  • linux

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.

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 initdb reports PostgreSQL 16.15, or the version you intentionally selected.
  • The cluster was initialised by its server owner, not root.
  • PG_VERSION contains 16 and the directory permissions are private.
  • pg_hba.conf uses an authentication method you chose, not an unexamined default.
  • Locale, encoding, checksums and WAL layout were treated as creation-time decisions.