Skip to content

config: move postgres tuning into compose, out of ALTER SYSTEM - #144

Merged
moogoo78 merged 1 commit into
mainfrom
devel
Jul 31, 2026
Merged

config: move postgres tuning into compose, out of ALTER SYSTEM#144
moogoo78 merged 1 commit into
mainfrom
devel

Conversation

@moogoo78

Copy link
Copy Markdown
Owner

Summary

Postgres tuning on production lived in two files inside the pgdata Docker volume, neither in version control:

setting value source
shared_buffers 128MB pgdata/postgresql.conf — initdb default, nobody set it
statement_timeout 30s pgdata/postgresql.auto.confALTER SYSTEM
log_autovacuum_min_duration 0 pgdata/postgresql.auto.confALTER SYSTEM

A fresh environment built from this repo would not have reproduced production. Command-line -c outranks both postgresql.conf and postgresql.auto.conf, so declaring them in compose makes the repo the single source of truth.

Why the memory settings are env-driven

compose.prod.yml is shared by production and staging, which are not the same size. A hardcoded shared_buffers=2GB could stop Postgres starting on a smaller box.

Defaults are the stock PostgreSQL values, so any environment that sets nothing behaves exactly as it does today — no surprise for staging. Production sets the tuned values in its .env:

POSTGRES_SHARED_BUFFERS=2GB
POSTGRES_EFFECTIVE_CACHE_SIZE=6GB
POSTGRES_MAINTENANCE_WORK_MEM=256MB

statement_timeout and log_autovacuum_min_duration are not size-dependent, so they're pinned for every deployed environment.

Why 2GB

Production is 8 GiB with a 3.4 GB database (named_area alone is 1.8 GB of PostGIS geometry). At 128MB, Postgres was caching almost nothing itself and leaning entirely on the OS page cache. Rule of thumb applied: shared_buffers ~25% of RAM, effective_cache_size ~75%.

Verification

Rendered config substitutes correctly both ways:

  • with env vars → shared_buffers=2GB, effective_cache_size=6GB, maintenance_work_mem=256MB
  • without → 128MB / 4GB / 64MB (stock defaults)

Booted locally with the production values; all five report the highest-precedence source:

 effective_cache_size        | 786432 | 8kB | command line
 log_autovacuum_min_duration | 0      | ms  | command line
 maintenance_work_mem        | 262144 | kB  | command line
 shared_buffers              | 262144 | 8kB | command line
 statement_timeout           | 30000  | ms  | command line

Follow-up after merge

  1. Add the three vars to production's .env (per-host config, like the existing secrets)
  2. Restart Postgres so they take effect — brief downtime
  3. Remove the two ALTER SYSTEM entries from postgresql.auto.conf so there is one source of truth rather than two silently competing

Also left alone deliberately: the inert docker/postgresql.conf mount. Uncommenting its config_file line would replace the image's entire default config and drop listen_addresses, breaking networking. Its pg_stat_statements settings could be moved onto this command line as a separate change.

🤖 Generated with Claude Code

Postgres tuning on production lived in two files inside the pgdata
Docker volume, neither in version control:

  shared_buffers=128MB              pgdata/postgresql.conf (initdb default)
  statement_timeout=30s             pgdata/postgresql.auto.conf (ALTER SYSTEM)
  log_autovacuum_min_duration=0     pgdata/postgresql.auto.conf (ALTER SYSTEM)

A fresh environment built from the repo would not have reproduced
production. Command-line `-c` outranks both postgresql.conf and
postgresql.auto.conf, so declaring them here makes the repo the single
source of truth.

Memory settings are env-driven because compose.prod.yml is shared by
production and staging, which are not the same size -- a hardcoded
shared_buffers=2GB could stop postgres starting on a smaller box.
Defaults are the stock PostgreSQL values, so any environment that sets
nothing behaves exactly as it does today. Production sets the tuned
values in its .env.

statement_timeout and log_autovacuum_min_duration are not size-dependent
and are pinned for every deployed environment.

Verified locally: rendered config substitutes correctly with and without
the env vars set, and postgres boots with all five reporting
`source = command line`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@moogoo78
moogoo78 merged commit fb39541 into main Jul 31, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant