DigitalOcean Managed PostgreSQL: Minimal HA Architecture and Operational Guardrails
Architecture note for the smallest production-ready DigitalOcean Managed PostgreSQL deployment: single-primary with one standby in a VPC, backups, PgBouncer, encryption, IAM roles, metrics alerts, and a quarterly failover drill. Includes doctl commands, firewall rules, and a verification checklist.
08 Mar 2026, 00:57 UTC

Problem and Takeaway
Teams adopting DigitalOcean Managed PostgreSQL often over-provision or under-monitor because the managed service hides infrastructure complexity. The smallest production-ready deployment is a single-primary, one-standby cluster in a VPC with backups enabled and PgBouncer for connection pooling. This design meets high-availability, backup, and isolation requirements while keeping monthly cost near $15 (db‑s‑1vcpu‑1gb plan). The operational burden shifts to verifying replication health, backup completeness, and failover behavior before traffic depends on it.
Requirements That Drive the Design
- High availability with automatic failover — the service must promote a standby within seconds when the primary becomes unreachable.
- Daily automated backups with point-in-time recovery — 7-day retention is the default; longer retention requires manual snapshots.
- Private network isolation — no public endpoint; access only from resources in the same VPC or peered VPCs.
- PostgreSQL version choice — currently 13 through 16; version upgrades are managed but require a maintenance window.
- Storage headroom — start with the minimum 10 GiB and enable auto-scaling or plan for manual volume increases.
Smallest Viable Deployment
Create a cluster with the following parameters (via control panel, doctl, or Terraform):
# doctl example (run from a workstation with API token)
doctl databases create pg-minimal \
--engine pg \
--version 16 \
--region nyc3 \
--size db-s-1vcpu-1gb \
--num-nodes 2 \
--private-network-uuid \
--backup-rules '{"schedule":"daily","window":"03:00-04:00"}'
Key choices:
--num-nodes 2provisions one primary and one synchronous standby in the same datacenter.--private-network-uuidplaces the cluster inside your VPC; the resulting hostname resolves to a private IP only.- Backup window set to low-traffic hours (03:00–04:00 UTC in the example).
- Enable the built-in PgBouncer connection pooler (port 6432) for any application that opens many short-lived connections.
Permissions: The API token needs Databases write scope. The VPC must already exist in the same region.
Trust and Data Boundaries
Encryption
Data at rest is encrypted with AES-256 (managed by DigitalOcean). In-transit TLS 1.2+ is enforced; the CA certificate is available from the cluster’s Connection Details page. Applications must verify the server certificate or pin the CA to prevent MITM.
Network Access
By default, the cluster accepts connections from any resource in the attached VPC. Restrict further with VPC firewall rules:
# Example: allow only the Kubernetes worker CIDR
doctl vpcs firewall-rules create \
--name allow-k8s-to-pg \
--direction inbound \
--protocol tcp \
--ports 25060,6432 \
--source-addresses 10.244.0.0/16 \
--description "K8s pods to PostgreSQL/PgBouncer"
Misconfigured VPC peering or overlapping CIDR blocks are the most common cause of "connection refused" errors that look like database downtime.
IAM and Database Roles
DigitalOcean IAM controls who can manage the cluster (create, delete, resize). Inside PostgreSQL, use separate roles:
-- Run as the default 'doadmin' user (admin)
CREATE ROLE app_readonly WITH LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE production TO app_readonly;
GRANT USAGE ON SCHEMA public TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app_readonly;
CREATE ROLE app_write WITH LOGIN PASSWORD '...';
GRANT app_readonly TO app_write;
GRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_write;
Applications connect as app_readonly or app_write; doadmin is reserved for schema migrations and manual administration.
Operational Checks
Metrics to Watch
DigitalOcean Metrics exposes the following at 1-minute granularity (no agent install required):
| Metric | Alert Threshold (example) | Why It Matters |
|---|---|---|
| Replication lag (bytes) | > 50 MB for > 5 min | Standby falling behind extends RPO |
| Backup status | Failed | Missing backups break point-in-time recovery |
| CPU utilization | > 80% sustained 15 min | Indicates need for vertical resize or query tuning |
| Memory utilization | > 85% | Risk of OOM kills on primary |
| Disk utilization | > 75% | Triggers auto-scale if enabled; otherwise manual action |
| Failover count | > 0 in 24h | Unexpected failovers signal instability |
Create alerts in the DigitalOcean control panel or via doctl monitoring alert create. Pair each alert with a runbook link.
Periodic Failover Drill
Once per quarter, trigger a controlled failover to verify application reconnection logic:
# Run from a bastion host or CI runner with doctl
doctl databases failover pg-minimal --confirm
Expected behavior: the standby promotes within 10–30 seconds; existing connections drop; new connections to the same hostname resolve to the new primary. Measure application recovery time (target < 30 s). Risk: this changes cluster state — schedule during a maintenance window and notify stakeholders.
Failure Modes and Mitigations
| Failure Mode | Detection | Mitigation | Residual Risk |
|---|---|---|---|
| Primary node hardware failure | Failover alert + replication lag spike | Automatic promotion of standby (synchronous replication ensures zero data loss) | Brief write unavailability (10–30 s) |
| Standby falls behind (network partition, heavy write load) | Replication lag alert | Alert on lag > 50 MB; investigate long-running transactions on primary | If primary fails before catch-up, potential data loss up to lag window |
| Backup job failure | Backup status alert | Retry runs automatically next window; manual snapshot via UI if urgent | Gap in point-in-time recovery coverage |
| VPC firewall rule blocks application CIDR | Connection timeouts from app logs | Test connectivity with psql -h -p 25060 -U app_readonly from app pod | Silent outage until detected |
| Major version upgrade maintenance window | Scheduled event notification (email/UI) | Schedule during low traffic; test app compatibility on staging cluster first | Brief connection drops (typically < 60 s) |
Conditions That Would Change This Design
- Read scaling needed — add read-only replicas (
--num-nodes 3+) and route reporting queries to them. - Cross-region disaster recovery — create a standby cluster in another region via
doctl databases create-replica(asynchronous, higher RPO). - Compliance requires customer-managed keys — not supported on Managed PostgreSQL; migrate to self-managed on Droplets or Kubernetes.
- Workload exceeds db-s-1vcpu-1gb — vertical resize (downtime ~2 min) or horizontal read replicas; monitor CPU/memory trends to decide.
- Application requires PostgreSQL extensions not in the supported list — verify current extension list; unsupported extensions force self-managed.
Verification Checklist Before Go-Live
- Cluster shows Healthy in control panel; both nodes report Running.
- Backups tab lists a successful daily backup after the first backup window.
- Replication lag metric stays near 0 bytes under normal write load.
- Application connects via PgBouncer (port 6432) and direct (port 25060) from the intended VPC CIDR.
- Failover drill completes; application recovers within SLA.
- Alerts fire in your notification channel (Slack, PagerDuty, email) for each threshold.
Limitations
- No control over PostgreSQL configuration parameters (e.g.,
work_mem,max_connections). - Major version upgrades are mandatory on DigitalOcean’s schedule; you cannot defer indefinitely.
- No built-in logical replication slots for external consumers (e.g., Debezium) — use a read replica if needed.
- Maximum storage per node is 4 TiB; beyond that requires sharding or self-managed.
Practical Result Check
After deployment and verification, run this query from an application host to confirm the topology:
psql "postgresql://app_readonly@:25060/production?sslmode=verify-full&sslrootcert=ca.crt" -c \
"SELECT application_name, state, sync_state, replay_lag FROM pg_stat_replication;"
Expected output: one row with sync_state = 'sync' and replay_lag near zero. If the row is missing, the standby is not attached — investigate immediately.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.