Here is a GUC that ships with a warning label. The docs, which are normally restrained to the point of parody, state plainly that setting this parameter wrong can cause “irretrievable data loss or seriously corrupt the database system.” When the PostgreSQL docs raise their voice, listen.

allow_system_table_mods is off by default. Turning it on lets a superuser perform DDL — ALTER TABLE, CREATE INDEX, and similar — against tables in pg_catalog. Without it, those operations are rejected even for superusers, which is the first layer of defense. This is the parameter you flip when you have decided that the system catalogs — the thing every query consults on its way to doing anything — need to be modified by hand.

Context is superuser in modern PostgreSQL, so you can SET allow_system_table_mods = on; in a session. That wasn’t always true; it used to require a server restart, which made emergency use awkward on a limping cluster.

What is it actually for? Two things:

  • initdb. The bootstrap process creates and populates the system catalogs. It needs this on to do its job.
  • Catalog surgery. When something has corrupted a catalog row and the cluster will not come up cleanly, editing pg_class, pg_attribute, or pg_depend by hand may be the only path back. The GUC exists so that, for the fifteen minutes a year a human needs to do this, they can.

For everything else — which is everything you will ever do — leave it off. Extensions that need to modify catalog contents have their own infrastructure and don’t touch this parameter. If someone on a forum tells you to turn it on to solve a normal problem, they are wrong.

If you do have to use it: take a file-level backup of $PGDATA first, do the work in a transaction, verify with SELECT before COMMIT, and do not assume that because the transaction committed the catalog is actually healthy. Run VACUUM FULL on the affected catalogs afterward and restart the cluster before you trust it.

Recommendation: Leave it off. If you need it, you are doing disaster recovery — in which case this blog post is not the document you should be reading.

(This is #3 in a series on every PostgreSQL GUC as of version 18, in alphabetical order.)