Configuration Tuning
pg tune turns a machine’s hardware specs and a workload class into a coherent set
of postgresql.conf parameters. It works against both kinds of deployment pgcli
manages: plain instances (written through the managed tuning block in
postgresql.conf) and Patroni HA clusters (written to the DCS via edit-config,
which rolls the change out to every member).
By default it only prints a recommendation — nothing is touched. --apply
writes it; --revert removes a previously applied tune.
When to use it
- A new instance after install — the container defaults are safe, not fast. One
pg tune -i <name> --applygets the memory partition, WAL sizing and planner cost constants into the right ballpark for the hardware. - After an up/down-size — RAM and CPU counts changed; re-run the same command and the derived values follow.
- When
pg inspectflags settings — the inspection report can point at undersizedshared_buffers/ low cache-hit ratios;pg tuneis the fix-it path.
Inputs
The specs default to a probe of the host the command runs on (total RAM from
/proc/meminfo on Linux, CPU count from the runtime). Overrides:
| Flag | Meaning |
|---|---|
--type |
workload class: web, oltp, dw, desktop, mixed (default) |
--hd |
storage class: ssd (default), hdd, san, nvme |
--ram |
total memory, e.g. 16G — use on shared hosts where the probe over- or under-reports |
--cpus |
CPU count — mandatory for cross-host Patroni clusters; compute against the smallest member |
--conns |
max_connections override (default: workload preset 200/300/40/20/100) |
--db-size |
database size vs RAM, e.g. 200G (<RAM → fits, ≤4×RAM → mid, else larger) |
Workload choice in one line: mostly short queries from an app → web/oltp;
analytics and big scans → dw; a single-user app co-located with the DB →
desktop; unsure → mixed.
The target server itself is probed for server_version_num and for every
parameter’s current value, so the table shows CURRENT vs RECOMMENDED side by side
and version-gated parameters (jit, io_method, …) only appear where they exist.
Applying
Managed instances (-i <name>)
--apply records the parameters in the instance’s config and writes them into
the managed tuning block of postgresql.conf. The block is re-applied on every
pg start, so the tuning survives restarts and reinstalls of the container.
Parameters set via ALTER SYSTEM (i.e. present in postgresql.auto.conf) are
skipped with a count — auto.conf always wins.
Changes to postmaster-level parameters (shared_buffers, wal_buffers,
max_connections, …) need a restart: interactively you’re asked y/N;
non-interactively pgcli only prints the pg stop && pg start hint and never
bounces the database on its own. Everything else is applied with a config
reload immediately.
Patroni clusters (--ha <scope>)
Here the parameters go into the cluster DCS config via
patronictl edit-config — the only sanctioned way to change postgresql.conf
values in a Patroni world. Patroni then applies them on every member, rolling
restarting for postmaster-level changes. Because that is disruptive, both
--apply and --revert ask for confirmation first; --auto-restart skips the
prompt (for scripts). If you decline, nothing is written to the DCS.
The recommendation table is computed from the host pgcli runs on — on a
cross-host cluster the members may differ, so pass --ram/--cpus matching the
smallest member.
Validation and --apply-conf
--apply-conf names a key = value file (postgresql.conf style: # comments,
quoted values, units allowed). Without --apply the file is written (the
header line records the inputs so the result is reproducible); with --apply the
file is read and becomes the parameter source.
Every apply path validates first. Each pair is checked against the target’s
pg_settings: unknown names (e.g. io_method on PG 15), non-boolean bools,
values outside an enum’s set, and integers/reals out of [min_val, max_val] are
listed per-parameter and the apply aborts wholesale — a partial write is the
worst outcome for a tuning batch.
--dsn: recommendations only
Remote targets are probe-and-suggest only; --apply/--revert are refused, since
writing a stranger’s server from a tuning heuristic is a decision pgcli will not
make for you.
--web-tune: the interactive site
Starts an HTTP server that serves the interactive tuning calculator. The page has a form for every input (version, OS, workload, RAM+unit, CPUs, connections, storage, db size), live recompute, and postgresql.conf / ALTER SYSTEM output with a copy button. No network access, no dependencies; the host’s RAM/CPU/OS prefill the form. The embedded calculator is a JS port of the Go algorithm; the page ships Go-computed golden fixtures and self-checks them on load, showing a red banner if the two implementations ever disagree.
Press Ctrl+C (or send SIGTERM) to stop the server gracefully.
How the numbers are derived
The formulas are faithful and deterministic (internal unit kB, flooring at each derivation step):
shared_buffers= RAM/4 (desktop: /16);effective_cache_size= 3/4 RAM.maintenance_work_mem= RAM/16 (dw: /8), capped at 8 GB (2047 MB on Windows ≤PG17).work_mem= (RAM − shared_buffers) / ((max_connections + max_worker_processes) × 3), then scaled by workload (dw/mixed ½, desktop ⅙) and db-size (fits ×1.3, larger ×0.9), floor 4 MB.wal_buffers= 3% of shared_buffers, clamped to [32 kB, 16 MB].random_page_cost4 for hdd and for dw-on-SSD (with a warning about catastrophic index scans), 1.1 otherwise;effective_io_concurrencyby storage class (Linux only).- Parallelism from CPU count (omitted below 4 cores);
autovacuum_max_workersat ≥16/≥32 cores; version gates:jit=off≥12,wal_compressionon/lz4 ≥10/≥15,io_method/io_workers≥18 (io_methodis alwaysworker—io_uringadditionally needs a--with-liburingbuild and kernel support, and a mismatch makes the server fail to start, so it is not auto-recommended), desktop-onlywal_level=minimal+max_wal_senders=0.
Per-parameter caveats (lz4 compile flag, the DW cost warning) are printed inline in the table; absurd machine sizes (<256 MB, >100 GB RAM) get a global warning.