Dependency
Overview
We need to upgrade the shared incubator-prod-database RDS instance from PostgreSQL 13.20 to 15, because 13.20 is deprecated and the instance is running on paid RDS Extended Support. Every HfLA project in the incubator shares this single instance, so the upgrade needs a planned window and a verified rollback path.
Action Items
Pre-flight checks. These need a psql session against the instance; the master credentials appear to be stored in the rds_credentials SSM parameter. Record the results in a comment on this issue — the post-upgrade verification compares against them.
Terraform changes, all in terraform/database.tf unless noted. Line numbers below were accurate when this was written and may drift; find each by attribute name.
Execution.
After the PR merges. These cannot be checked from a branch — the upgrade only happens on merge, so the assignee has to come back to them.
Resources/Instructions
terraform/database.tf — the RDS instance declaration.
terraform/modules/database/ — the per-project module that creates logical databases and roles on this instance. It reaches the instance through a data source with a hardcoded identifier, so it has no dependency edge on the resource.
.github/workflows/terraform-apply.yaml — the apply-on-merge workflow.
- AWS: account
035866691871 (incubator), region us-west-2, instance incubator-prod-database.
Verified facts as of this ticket's creation:
- 13.20 reports
Status: deprecated; the instance carries engine_lifecycle_support = "open-source-rds-extended-support".
- 13.20 → 15.x is a valid direct upgrade target — no intermediate hop through 14 is required. 15.13 through 15.18 are
available; 15.12 and below are deprecated.
- This is the only RDS instance in the account, shared by all incubator projects.
- Confirm the current target list before starting, since it moves:
aws rds describe-db-engine-versions --engine postgres --engine-version 13.20 --region us-west-2 --include-all --query 'DBEngineVersions[0].ValidUpgradeTarget[?IsMajorVersionUpgrade==\true`].EngineVersion'`
Rollback. There is no in-place downgrade. Rollback means restoring the incubator-prod-database-pre-pg15 snapshot from the dependency issue to a new instance and repointing consumers — roughly 30+ minutes, and any writes made after the snapshot are lost. That is why the window should be quiet and why the snapshot is a hard prerequisite.
Alternative considered — RDS Blue/Green. Would cut downtime to about a minute, but requires a custom parameter group with rds.logical_replication = 1 plus a reboot to apply it, and doubles instance cost while both environments are live. Not proposed here: the data volume is small and most consumers are dev/qa/stage. Reconsider only if vrms-backend-prod or cti-backend-prod cannot take the window.
Dependency
Overview
We need to upgrade the shared
incubator-prod-databaseRDS instance from PostgreSQL 13.20 to 15, because 13.20 is deprecated and the instance is running on paid RDS Extended Support. Every HfLA project in the incubator shares this single instance, so the upgrade needs a planned window and a verified rollback path.Action Items
Pre-flight checks. These need a
psqlsession against the instance; the master credentials appear to be stored in therds_credentialsSSM parameter. Record the results in a comment on this issue — the post-upgrade verification compares against them.\l), so you can prove afterwards that they all survived.SELECT * FROM pg_extension;). Some extensions blockpg_upgradeor need their own version handling — PostGIS in particular. Check anything non-stock against the postgres15 family before scheduling the window.SELECT slot_name FROM pg_replication_slots;). Any existing slot causespg_upgradeto fail and must be dropped first.reg*columns (regproc,regclass, and similar).pg_upgradecannot carry these across and will fail.password_encryptionfrommd5toscram-sha-256. Existing roles keep their md5 hashes through the upgrade so nothing breaks on day one, but roles created afterwards may get scram, which older client drivers (old node-pg, old psycopg2) cannot authenticate with — andterraform/modules/databasecreates roles for every new project. Check the value in the postgres15 parameter family and record the decision here.Terraform changes, all in
terraform/database.tfunless noted. Line numbers below were accurate when this was written and may drift; find each by attribute name.allow_major_version_upgrade = true. It is absent today, and without it the apply fails outright on a major version bump.engine_versionfrom"13.20"to"15". Pin the major version only —auto_minor_version_upgrade = trueis already set, so pinning a full minor guarantees drift the next time AWS patches the instance.parameter_group_name = "default.postgres13"with a postgres15-family group. RDS rejects a major upgrade that keeps an old-family parameter group. Prefer adding anaws_db_parameter_groupresource withfamily = "postgres15"over referencingdefault.postgres15: that default group does not exist in the account yet (onlydefault.postgres13does — AWS creates defaults lazily), and a managed group gives future tuning somewhere to live.option_group_namefrom"default:postgres-13"to"default:postgres-15"— same family constraint. Option groups do nothing for Postgres, but the attribute is declared, so it has to move with the family.engine_lifecycle_support. Leaving it at"open-source-rds-extended-support"means paying Extended Support again once 15 reaches end of standard support;"open-source-rds-extended-support-disabled"means AWS force-upgrades at that point instead of billing. Record which was chosen and why in a comment here.importblock at the top of the file (lines 1-4 as of writing; find it byimport {). It was the one-time adoption of the pre-existing instance and has already run — it is stale, not load-bearing.apply_immediately = truestays for this change. Left as-is, the upgrade begins the moment the merge lands. Set tofalseand RDS defers it to themon:00:00-mon:03:00maintenance window instead. Either is defensible — the point is to choose, rather than let the existing value decide.Execution.
db.t3.smallis typically 10-20 minutes including RDS's pre- and post-upgrade reboots..github/workflows/terraform-apply.yamlrunsterraform applywithauto_approve: trueon every push tomaintouching a.tffile. There is no separate apply step to schedule.terraform-planoutput on the PR and confirm it is an in-place update toaws_db_instance.default. A plan proposing to replace this resource must not be merged —skip_final_snapshot = trueanddeletion_protection = falsemean nothing would stop a destroy of the shared database.aws rds describe-events --source-identifier incubator-prod-database --source-type db-instance --region us-west-2.After the PR merges. These cannot be checked from a branch — the upgrade only happens on merge, so the assignee has to come back to them.
aws rds describe-db-instances --db-instance-identifier incubator-prod-database --region us-west-2 --query 'DBInstances[0].EngineVersion'.ANALYZEon every database.pg_upgradedoes not carry optimizer statistics across, so query plans will be poor until this runs. This is the most commonly missed step of a major version upgrade.terraform/projects/civic-tech-index/environment-stage.tfandenvironment-prod.tfhardcode the hostname inPOSTGRES_HOST, so a changed endpoint would silently break those two services.vrms-backend-prodandcti-backend-prod.terraform planand confirm it is clean. Ifengine_versionorparameter_group_nameshows drift, resolve it now rather than leaving it for whoever plans next.Resources/Instructions
terraform/database.tf— the RDS instance declaration.terraform/modules/database/— the per-project module that creates logical databases and roles on this instance. It reaches the instance through adatasource with a hardcoded identifier, so it has no dependency edge on the resource..github/workflows/terraform-apply.yaml— the apply-on-merge workflow.035866691871(incubator), regionus-west-2, instanceincubator-prod-database.Verified facts as of this ticket's creation:
Status: deprecated; the instance carriesengine_lifecycle_support = "open-source-rds-extended-support".available; 15.12 and below are deprecated.aws rds describe-db-engine-versions --engine postgres --engine-version 13.20 --region us-west-2 --include-all --query 'DBEngineVersions[0].ValidUpgradeTarget[?IsMajorVersionUpgrade==\true`].EngineVersion'`Rollback. There is no in-place downgrade. Rollback means restoring the
incubator-prod-database-pre-pg15snapshot from the dependency issue to a new instance and repointing consumers — roughly 30+ minutes, and any writes made after the snapshot are lost. That is why the window should be quiet and why the snapshot is a hard prerequisite.Alternative considered — RDS Blue/Green. Would cut downtime to about a minute, but requires a custom parameter group with
rds.logical_replication = 1plus a reboot to apply it, and doubles instance cost while both environments are live. Not proposed here: the data volume is small and most consumers are dev/qa/stage. Reconsider only ifvrms-backend-prodorcti-backend-prodcannot take the window.