PostgreSQLIII · Configuration ArchitectureConfiguration
ALTER SYSTEM, and how it fails
What you'll learn
- Describe what ALTER SYSTEM writes and where
- Enumerate the errors ALTER SYSTEM raises and, critically, the one it does not
- Explain why a restart-only change is a deferred risk rather than an immediate one
- Decide whether ALTER SYSTEM should be permitted in a managed estate
Prerequisites
Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27
ALTER SYSTEM is the supported way to change server configuration from
a SQL connection, without shell access to the host. It is genuinely
useful — a managed service or a locked-down host may offer no other
route — and it has one failure mode serious enough to justify a lesson
of its own.
What it does
It writes a single key to postgresql.auto.conf in the data directory,
rewriting that file wholesale each time. The file is read last, after
postgresql.conf and everything it includes, so an ALTER SYSTEM
entry overrides them.
ALTER SYSTEM SET max_wal_size = '4GB'; -- write it
ALTER SYSTEM RESET max_wal_size; -- remove that one key
ALTER SYSTEM RESET ALL; -- empty the file
It does not apply the change. Applying still requires a reload for
a sighup parameter or a restart for a postmaster one, exactly as if
you had edited the file by hand.
The errors it does raise
These are all rejections at statement time, which is the helpful case:
$ psql -U postgres # four separate ALTER SYSTEM attempts-- inside a transaction block
ERROR: ALTER SYSTEM cannot run inside a transaction block
-- a value that does not parse
ALTER SYSTEM SET work_mem = 'banana';
ERROR: invalid value for parameter "work_mem": "banana"
-- a read-only parameter
ALTER SYSTEM SET block_size = 16384;
ERROR: parameter "block_size" cannot be changed
-- a parameter that does not exist
ALTER SYSTEM SET not_a_real_parameter = 1;
ERROR: unrecognized configuration parameter "not_a_real_parameter"The transaction-block rejection is worth remembering practically: it
means several ALTER SYSTEM statements cannot be passed in one psql -c argument, because psql wraps multiple statements in a single
transaction. Use a separate -c for each.
The error it does not raise
ALTER SYSTEM validates that a value is syntactically legal for the
parameter. It does not validate that the machine can satisfy it.
The parameters where this bites are the postmaster-context ones that
size shared memory: shared_buffers, max_connections,
max_locks_per_transaction, wal_buffers, max_worker_processes. A
sighup parameter set to an unreasonable value is far less dangerous,
because the reload applies it immediately and you find out at once.
Disabling ALTER SYSTEM
PostgreSQL provides a parameter to turn the command off entirely.
$ psql -U postgres -c "SELECT name, setting, context, short_desc FROM pg_settings WHERE name = 'allow_alter_system'" name | setting | context | short_desc
--------------------+---------+---------+------------------------------------------
allow_alter_system | on | sighup | Allows running the ALTER SYSTEM command.
(1 row)Setting it to off — in postgresql.conf, not via ALTER SYSTEM —
makes the command raise an error. This is worth doing in one specific
situation: an estate where configuration is owned by Ansible, Terraform
or a GitOps pipeline, and where an ALTER SYSTEM made during an
incident would silently diverge the running server from the repository
that everyone believes describes it.
It is explicitly not a security control. The documentation is direct about this: a superuser has many other ways to change server behaviour, so this prevents accident and drift rather than a determined actor. Treat it as a guard rail, in the same category as a protected branch.
Production discipline
- Check
pending_restartimmediately after everyALTER SYSTEM. It is the only signal distinguishing a change that is live from one that is merely written. - Never leave a restart-only change pending indefinitely. Restart in a window you chose rather than discovering the outcome during an unplanned one.
- Treat
ALTER SYSTEMon shared-memory parameters as the highest-risk configuration action available, because it is the one whose failure is deferred and total. - Use one
-cperALTER SYSTEM. Several statements in a single-cbecome one transaction, which the command refuses. - Consider
allow_alter_system = offwhere configuration is managed as code, as a drift guard rail rather than a security control. - Editing
postgresql.auto.confby hand is legitimate exactly once: when the server will not start because of what is in it.
Cross-course references
- Git, CI/CD & GitOps — Part LXXV (Drift) covers the divergence
between a repository and a running system that
allow_alter_systemexists to prevent, and Part CVI (Change management) covers stating a rollback for a change whose effect is deferred. - Ansible for Production Sysadmins — Part XXXVI (Drift) covers detecting and correcting exactly this class of out-of-band change.
- Linux for Production Sysadmins — Part XXXVII (Resources) and Part XL (Memory performance) cover the shared-memory limits that the failure in this lesson runs into.
Quiz
Knowledge check · 6 questions
Q1. An operator runs ALTER SYSTEM SET shared_buffers = '999GB' on an ordinary server, reloads, and sees no error. What is the state of the cluster?
Q2. A cluster will not start because a value written by ALTER SYSTEM is impossible. What is the correct recovery?
Q3. Which of these does ALTER SYSTEM reject at statement time? Select all that apply.
Q4. Setting allow_alter_system to off is a security control that prevents a superuser from changing server configuration.
Q5. Explain why a restart-only parameter is riskier to set via ALTER SYSTEM than a reload-level one, and name the column that reveals the pending state.
Q6. Diagnose and give both the immediate recovery and the process change.
At 02:40 a host reboots after a hypervisor fault. PostgreSQL does not come back. The log shows a FATAL error about mapping anonymous shared memory, with a hint mentioning shared_buffers and max_connections. The on-call engineer confirms nobody has touched the database in weeks and the last change record is 41 days old. The monitoring history shows the cluster ran normally for the entire 41 days. The host has 64 GB of memory and postgresql.conf reads shared_buffers = 16GB.
Passing score: 75%. Answers are checked in this browser.