initdb & Data Directory
initdb creates a new PostgreSQL cluster on disk. The data directory (PGDATA) holds all databases, WAL, and configuration for that single instance.
Recipe
sudo -u postgres mkdir -p /var/lib/postgresql/18/main
sudo -u postgres /usr/lib/postgresql/18/bin/initdb \
-D /var/lib/postgresql/18/main \
--encoding=UTF8 \
--locale=C.UTF-8 \
--data-checksumsWhen to reach for this: First cluster on a host, restoring to a blank data directory, or building a standby before pg_basebackup.
Working Example
# Create dedicated mount points (production pattern)
sudo mkdir -p /data/pgdata /data/pgwal
sudo chown postgres:postgres /data/pgdata /data/pgwal
sudo chmod 700 /data/pgdata /data/pgwal
sudo -u postgres initdb \
-D /data/pgdata \
--waldir=/data/pgwal \
--encoding=UTF8 \
--locale=en_US.UTF-8 \
--data-checksums
# Confirm layout
sudo -u postgres ls -la /data/pgdata
sudo -u postgres ls -la /data/pgdata/baseWhat this demonstrates:
- Separate
PGDATAand WAL on different volumes for IO isolation 700permissions on data dirs (owner-only access)--data-checksumsenables page checksum verification at read timebase/subdirectory appears after init - one subdir per database OID
Deep Dive
PGDATA Layout (Key Paths)
| Path | Purpose |
|---|---|
postgresql.conf | Main server configuration |
pg_hba.conf | Client authentication rules |
pg_ident.conf | User name maps |
base/ | Per-database data files |
global/ | Cluster-wide catalogs |
pg_wal/ | Write-ahead log (unless --waldir) |
pg_xact/ | Transaction commit status |
PG_VERSION | Major version marker |
Permissions Model
# Correct ownership
ls -ld /data/pgdata
# drwx------ postgres postgres
# Wrong: group or world readable
chmod 750 /data/pgdata # avoid - leaks catalog metadata risk- Only the
postgresOS user should read/writePGDATA. - Backup tools run as
postgresor use filesystem snapshots with correct UID. - Never run
initdbas root - the server refuses to start on root-owned data.
Locale and Encoding
-- Set at init time; changing later requires re-init
SHOW server_encoding; -- UTF8
SHOW lc_collate; -- en_US.UTF-8
SHOW lc_ctype;- Prefer
UTF8encoding for all new clusters. CorC.UTF-8collation gives predictable sort order; locale-specific collation affects index order.- ICU locales (
--locale-provider=icu) are available in PostgreSQL 15+.
WAL Directory Separation
# postgresql.conf (if not set at initdb)
# wal_directory is set at init via --waldir; relocate carefully with pg_resetwal only in disaster recovery- WAL is append-heavy; data files are random IO - separate SSD/NVMe pools when IO-bound.
- Document both paths in your inventory - restores must recreate the same layout.
Gotchas
- Re-running initdb on existing PGDATA -
initdbfails if the directory is non-empty. Fix: Use a fresh empty directory orpg_basebackupfor replicas. - Wrong ownership after restore -
chownfrom backup tar as root leaves root-owned files. Fix:chown -R postgres:postgres $PGDATAbefore start. - Missing data checksums - Clusters created without
--data-checksumscannot enable them later without re-init. Fix: Enable at init; plan checksum-aware monitoring. - Locale mismatch across replicas - Standby init with different
lc_collatebreaks index ordering assumptions. Fix: Match initdb flags on all nodes in a topology. - Full disk on PGDATA -
initdbneeds scratch space; running cluster needs headroom for WAL and temp files. Fix: Monitor filesystem % used; keep WAL on separate volume.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Package-managed init (Debian pg_createcluster) | Standard apt installs | You need custom paths on day one |
pg_basebackup | Standby or PITR seed | Brand-new empty primary with no source |
| Managed cloud "Create instance" | No host access | You need filesystem-level control |
FAQs
What is PGDATA vs a database?
PGDATA is one cluster (instance). A cluster contains many databases (postgres, myapp, etc.). One postgres process manages one PGDATA.
Can I move PGDATA after init?
Yes, with care: stop the server, move the directory, update systemd Environment=PGDATA=, or use pg_ctl -D. Do not move live files while the server runs.
Why chmod 700?
Catalog files contain schema metadata and connection info. World-readable data directories are a finding in security audits.
Should I use --data-checksums?
Yes for new production clusters. Small CPU cost buys early detection of silent storage corruption.
Where does WAL go with --waldir?
WAL segments live under the path you pass to --waldir, symlinked from $PGDATA/pg_wal. Both paths must be available at start.
Related
- Installation Basics - package installs before initdb
- postgresql.conf Essentials - tune after first boot
- pg_hba.conf & Authentication - allow connections
- Systemd & Service Management - start the new cluster
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.