How to Fix AWS RDS PostgreSQL 'No Route to Host' Connection Error
Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–20 mins depending on root cause
TL;DR
- What broke: Your compute resource (EC2, Lambda, ECS task) cannot establish a TCP connection to RDS port 5432. The OS-level error
No route to hostmeans a firewall (Security Group or NACL) is actively dropping packets or there is no valid network path — this is not a DNS or credential issue. - How to fix it: Audit the RDS Security Group inbound rules, the client Security Group outbound rules, subnet route tables, and NACLs. One of these is blocking TCP 5432 between your client and the RDS ENI.
- Fast path: Use our Client-Side Sandbox below to auto-refactor your Terraform security group and RDS module config — paste your failing
.tfand get corrected ingress rules generated locally without sending your VPC IDs or DB strings anywhere.
The Incident
Raw error:
psql: error: could not connect to server: No route to host
Is the server running on host "mydb.cxyz123.us-east-1.rds.amazonaws.com" (10.0.3.47)
and accepting TCP/IP connections on port 5432?
This is not a PostgreSQL authentication error. The TCP handshake never completes. The client's kernel received an ICMP host unreachable or the SYN packet was silently dropped and timed out. Your application is dead in the water — connection pool exhaustion follows within seconds in production, cascading into full service unavailability.
The Attack Vector / Blast Radius
Why this kills production:
- Every application thread waiting on a DB connection blocks. HikariCP, pgBouncer, SQLAlchemy pools all hit
connectionTimeoutsimultaneously. - If this fires during a deployment, your new task revisions fail health checks, triggering a rollback loop.
- Security angle: This error is also the correct behavior you want when an unauthorized host tries to reach RDS — but when it hits your own app, it means your Security Group rules have drifted, a Terraform apply partially failed, or someone manually edited SG rules in the console and broke the reference chain.
- In multi-account VPC peering or Transit Gateway setups, a missing route table entry is the #1 cause. The DNS resolves fine, the private IP is reachable on paper, but the return path route is absent — packets go in, responses never come back.
The four failure points, in order of frequency:
- RDS Security Group missing inbound rule for TCP 5432 from the client SG or CIDR
- Client Security Group missing outbound rule for TCP 5432 (default outbound allows all, but explicit deny or restrictive egress breaks this)
- NACL stateless rules blocking ephemeral return ports (1024–65535) on the subnet
- Route table missing entry for the RDS subnet (cross-AZ, peered VPC, or TGW scenario)
How to Fix It
Basic Fix — Security Group Inbound Rule (AWS Console / CLI)
The RDS instance's Security Group must allow inbound TCP 5432 from the Security Group attached to your client, not from 0.0.0.0/0.
# AWS CLI — add missing ingress rule to RDS security group
- # No inbound rule exists for port 5432
+ aws ec2 authorize-security-group-ingress \
+ --group-id sg-0abc123def456 \
+ --protocol tcp \
+ --port 5432 \
+ --source-group sg-0zzz789app111
Enterprise Best Practice — Terraform Security Group with Least-Privilege SG Reference
Never use CIDR blocks for internal RDS access. Always reference the application Security Group ID directly. This prevents lateral movement if the app subnet CIDR is ever reused.
resource "aws_security_group_rule" "rds_ingress_postgres" {
type = "ingress"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_group_id = aws_security_group.rds.id
- cidr_blocks = ["10.0.0.0/8"] # Too broad, entire RFC1918 range
+ source_security_group_id = aws_security_group.app.id # Scoped to app tier only
}
# Also verify: explicit egress on the APP security group if you've locked it down
resource "aws_security_group_rule" "app_egress_postgres" {
type = "egress"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_group_id = aws_security_group.app.id
- # MISSING — egress rule absent when default egress was removed
+ source_security_group_id = aws_security_group.rds.id
}
NACL Fix — Stateless Ephemeral Port Return Traffic
NACLs are stateless. If you've restricted your RDS subnet NACL, you must explicitly allow outbound ephemeral ports back to the client.
resource "aws_network_acl_rule" "rds_subnet_egress_ephemeral" {
network_acl_id = aws_network_acl.rds_subnet.id
rule_number = 100
egress = true
protocol = "tcp"
- # Rule missing entirely — return traffic dropped
+ from_port = 1024
+ to_port = 65535
+ cidr_block = "10.0.1.0/24" # App subnet CIDR
+ rule_action = "allow"
}
Route Table Fix — VPC Peering / TGW Cross-Account
resource "aws_route" "app_to_rds_vpc" {
route_table_id = aws_route_table.app_subnet.id
destination_cidr_block = "10.1.3.0/24" # RDS subnet in peered VPC
- # Route missing — traffic hits local VPC boundary and drops
+ vpc_peering_connection_id = aws_vpc_peering_connection.app_to_data.id
}
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
Checkov — catch missing SG ingress before terraform apply:
# .checkov.yml
checks:
- CKV_AWS_25 # Ensure no security groups allow ingress from 0.0.0.0/0 to port 5432
- CKV_AWS_382 # Ensure RDS is not publicly accessible
- CKV2_AWS_5 # Ensure SGs are attached to resources (detached SGs = drift indicator)
OPA / Conftest policy — enforce SG reference over CIDR for RDS:
package terraform.aws.rds
deny[msg] {
rule := input.resource.aws_security_group_rule[_]
rule.config.from_port == 5432
rule.config.cidr_blocks != null
msg := "RDS port 5432 ingress must use source_security_group_id, not cidr_blocks"
}
GitHub Actions — block PR merge on SG drift:
- name: Checkov IaC Scan
uses: bridgecrewio/checkov-action@master
with:
directory: ./terraform
check: CKV_AWS_25,CKV_AWS_382
soft_fail: false # Hard fail the pipeline
Operational checklist to run before every RDS-touching deployment:
aws ec2 describe-security-groups --group-ids <rds-sg-id>— verify port 5432 inbound rule exists and references the correct source SGaws ec2 describe-network-acls --filters Name=association.subnet-id,Values=<rds-subnet-id>— verify ephemeral egress rule presentaws ec2 describe-route-tables --filters Name=association.subnet-id,Values=<app-subnet-id>— verify route to RDS subnet existsnc -zv <rds-endpoint> 5432from the app host — TCP reachability test before starting the app process