PostgreSQLXVIII · Platforms, Corruption and Production ArchitecturePlatforms
Automating PostgreSQL, and change management for database change
What you'll learn
- Decide what to automate and what to keep manual
- Apply expand-and-contract to make schema change reversible
- Make migrations safe by default rather than by discipline
- Separate configuration management from state management
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
Almost everything in this course can be automated. Some of it should not be.
What to automate
Anything repetitive and reversible:
- Cluster provisioning and configuration.
- Backups, and the restore tests from lesson XIII-08.
- Monitoring and alerting definitions.
- Minor version upgrades — lesson XVII-05’s binary swap.
- Routine maintenance windows.
- Schema migrations, with the safeguards below.
Anything nobody should have to remember:
lock_timeouton the migration role, from lesson XVII-04.idle_in_transaction_session_timeouton application roles.- Retention that is bounded by the oldest backup, from lesson XIII-05.
What not to automate
Failover decisions on a single important cluster. Lesson XV-04’s arithmetic: the most common HA-caused outage is failing over when nothing was wrong.
Anything destructive without a human. VACUUM FULL, DROP,
pg_resetwal, delete_old_cluster.sh.
Responding to corruption. Lesson XVIII-05: almost everything that feels like fixing it destroys evidence.
Killing sessions by rule. A script that terminates anything running over N seconds will eventually terminate the migration, the backup, or the report that matters.
Expand and contract
The pattern that makes schema change reversible. Renaming a column, without a moment where old and new code cannot both run:
-- 1. EXPAND: add the new column. Reversible; nothing reads it yet.
ALTER TABLE users ADD COLUMN email_address text;
-- 2. BACKFILL, in batches (lesson XVII-04)
UPDATE users SET email_address = email
WHERE id IN (SELECT id FROM users WHERE email_address IS NULL LIMIT 10000);
-- 3. DUAL WRITE: application writes both columns. Deploy, and let it soak.
-- 4. SWITCH READS to the new column. Deploy. Still fully reversible.
-- 5. STOP WRITING the old column. Deploy.
-- 6. CONTRACT: after long enough to be certain.
ALTER TABLE users DROP COLUMN email;
Steps 1 to 5 are reversible at every point, and each is a separate deployment. Only step 6 is not, and by then the column has been unused long enough that dropping it is a decision rather than a risk.
It is slower than renaming the column. It is slower in exchange for never having a moment where a rollback is impossible, and on a system where downtime is expensive that trade is not close.
Making migrations safe by default
-- on the role migrations run as
ALTER ROLE migrator SET lock_timeout = '3s';
ALTER ROLE migrator SET statement_timeout = '30s';
Lesson XVII-04 measured why: a waiting ALTER TABLE blocks every query
behind it, so an unbounded lock wait is an outage. Setting it on the
role means the least careful migration anyone writes still fails loudly
rather than stalling the site.
A checklist worth enforcing in review:
- Does it rewrite the table? (lesson XVII-04’s table)
- Does it take
ACCESS EXCLUSIVE, and for how long? - Is
lock_timeoutset? - Is any backfill batched?
- Is it reversible? If not, is that intended and agreed?
- Has it run against a copy of production-sized data?
- Can the previous application version run against the new schema?
That last one is what makes deployments independent of migrations, and it is the question expand-and-contract exists to keep answerable.
What to take from this
- Automate the repetitive and reversible; keep judgement manual.
- Database change is not application change. A dropped column does not come back with a deployment.
- Expand and contract makes every step reversible except the last, and puts the last one weeks later.
- Set
lock_timeouton the migration role, so safety is the default rather than a habit. - Review for rewrite, lock, batching, reversibility, and whether the previous application version still runs.
- Configuration management handles packages and files. Schema needs a migration tool; data needs neither.
ALTER SYSTEMwrites a file configuration management does not manage. Checkpg_settings.source.
Cross-course references
- Ansible for Production Sysadmins — Part XII (Idempotency), Part XXXVI (Drift) and Part XXX (Host targeting and blast radius) cover automating configuration without automating an outage.
- Git, CI/CD & GitOps — Part CVI (Change management), Part LVII (Approval gates) and Part LXXV (Drift) cover treating a schema change as a delivery with a review and a record.
- Linux for Production Sysadmins — Part LXXIV (Configuration drift) covers detecting the setting somebody changed during an incident and never put back.
Quiz
Knowledge check · 6 questions
Q1. A migration dropped a column, and the deployment is rolled back to the previous application version. What is the state of the data?
Q2. Why must a migration be backward compatible with the application version currently deployed?
Q3. A configuration management run reports the cluster converged, but a setting an operator changed during an incident is still in effect. Why?
Q4. Which should generally NOT be automated? Select all that apply.
Q5. A down migration that reverses a backfill restores the original values.
Q6. Describe expand-and-contract for renaming a column, and explain what it buys.
Passing score: 75%. Answers are checked in this browser.