Cloud SQL removes host and storage administration; it does not remove database engineering. The dangerous migration is the one that transfers PostgreSQL unchanged while leaving failover, connection recovery, and service limits untested.

Situation

A team moving PostgreSQL from Compute Engine already knows how to tune SQL and operate backups. Cloud SQL takes responsibility for VM lifecycle, supported engine maintenance, storage management, and managed availability. The application still owns connection behavior, transaction retries, query design, capacity planning, and recovery validation.

That boundary matters because Cloud SQL is a service contract, not a VM with root access. DBAs cannot install arbitrary host software, replace the storage subsystem, or assume that a setting accepted by self-managed PostgreSQL is configurable in the service.

The Problem

The most common design error is to reduce the migration decision to “PostgreSQL is PostgreSQL.” SQL compatibility does not make the operating model identical. A zonal instance has a different failure boundary from a regional HA instance. A read replica is not an HA standby. The Cloud SQL Auth Proxy authenticates and encrypts connections, but it is not a connection pool. A maintenance event or failover terminates existing sessions even when the endpoint remains stable.

The architectural question is therefore: which responsibilities moved to Google, which remained with the workload, and how will the team prove the boundary under failure?

The Managed-Service Boundary

flowchart TD
    App[Application] --> Pool[Application connection pool]
    Pool --> Connector[Cloud SQL connector or Auth Proxy]
    Connector --> Endpoint[Cloud SQL instance endpoint]
    Endpoint --> Primary[Primary — zone A]
    Primary --> PrimaryDisk[(Persistent disk — zone A)]
    Primary -->|Synchronous write| StandbyDisk[(Persistent disk — zone B)]
    StandbyDisk --> Standby[Standby — zone B]
    Primary --> Replica[Optional read replica]

For a regional HA instance, Google runs a primary and standby in different zones and synchronously replicates writes to regional persistent disk. The standby is not an application-readable replica. If the primary becomes unresponsive, Cloud SQL can fail over and retain the instance connection name and IP address; existing connections still close and must reconnect. Google documents that reconnection is typically possible in about 60 seconds, but that is an observation to test, not an application SLA to hard-code.

Read replicas solve a different problem. They serve read traffic and use asynchronous replication, so they can lag and return stale results. Cross-region replicas can form part of disaster recovery, but promotion is an explicit recovery action and produces a new primary endpoint.

The connectivity layer also has a precise boundary. Cloud SQL connectors and the Auth Proxy provide IAM-aware authorization and TLS without requiring the application to manage certificates. They do not restrict the number of PostgreSQL backends. The application still needs a bounded pool, timeouts, retry policy, and admission control. PgBouncer is one possible pooler, but it is an additional component with its own failure and transaction-pooling semantics.

Document the boundary in the service ownership map: Google owns the managed host and control plane; the platform team owns network and IAM policy; the database team owns schemas and capacity; and the application team owns transaction semantics, pooling, and retries. Ambiguous ownership is itself a failure mode during an outage.

In Practice

Define the instance declaratively, but keep environment-specific sizing out of a reusable example:

resource "google_sql_database_instance" "postgres" {
  name                = "production-postgres"
  database_version    = "POSTGRES_15"
  region              = "us-central1"
  deletion_protection = true

  settings {
    tier              = "db-custom-8-32768"
    availability_type = "REGIONAL"
    disk_type          = "PD_SSD"
    disk_autoresize    = true

    backup_configuration {
      enabled                        = true
      point_in_time_recovery_enabled = true
      start_time                     = "03:00"
    }

    maintenance_window {
      day          = 7
      hour         = 4
      update_track = "stable"
    }
  }
}

This configuration is a starting point, not proof of readiness. Pin the provider version, review the selected PostgreSQL version’s support policy, validate the tier in the target region, and plan how Terraform state and deletion protection are controlled.

The CARL evidence here is system behavior documented by Google: regional HA closes sessions during failover; maintenance can interrupt connectivity; replicas can lag; and connectors are not poolers. Convert those behaviors into tests:

  1. Trigger a manual failover under representative traffic and measure reconnect time, retry volume, and tail latency.
  2. Exhaust the configured application pool in staging and verify load is rejected rather than expanding unbounded connections.
  3. Measure replica lag and test a read-after-write request against the replica path.
  4. Restore a backup or PITR copy and verify application-level integrity, not merely instance creation.

Where It Breaks

Failure modeEvidence to inspectEngineering response
Connection storm after failoverBackend count, pool queue depth, retry rateAdd jittered retry, cap pools, and avoid every process reconnecting simultaneously.
Storage saturationRead and write latency, throughput, queue depthTune queries first; then size storage and compute from measured demand. Do not prescribe an arbitrary disk size.
Stale replica readsReplica lag and application consistency errorsRoute read-after-write and correctness-sensitive queries to the primary.
Maintenance surpriseMaintenance event logs and client disconnectsConfigure windows and deny periods where appropriate; make disconnect recovery routine.
Terraform destroys or replaces an instancePlan output and state historyRequire reviewed plans, deletion protection, backups, and a tested recovery path.

What to Do Next

  • Problem: The team has transferred the database but not redesigned around the managed-service failure boundary.
  • Solution: Treat HA, connection management, read scaling, and recovery as separate capabilities with separate tests.
  • Proof: Google’s documented HA behavior explicitly closes existing connections while retaining the endpoint, and replicas explicitly permit lag.
  • Action: Run a failover and restore exercise before production cutover, recording measured RTO, observed RPO, retry behavior, and pool saturation.

Sources to Verify