# Fix AWS RDS Connection Timeout
You're staring at your application logs and see "Connection timed out" or "Could not connect to database server" when trying to reach your RDS instance. The instance is running, your application worked before, and now everything is timing out.
RDS connection timeouts have several common causes: security group misconfiguration, VPC networking issues, DNS problems, or database resource exhaustion. Let's work through each systematically.
Diagnosis Commands
First, verify the RDS instance is actually running:
aws rds describe-db-instances \
--db-instance-identifier my-database \
--query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceStatus,Endpoint.Address,Endpoint.Port]' \
--output tableIf the status is "available," the instance is running. Note the endpoint address for testing.
Check the instance's security group:
aws rds describe-db-instances \
--db-instance-identifier my-database \
--query 'DBInstances[*].VpcSecurityGroups[*].[VpcSecurityGroupId,Status]' \
--output tableExamine the security group rules:
aws ec2 describe-security-groups \
--group-ids sg-12345678 \
--query 'SecurityGroups[*].IpPermissions[*].[FromPort,ToPort,IpProtocol,IpRanges[*].CidrIp]' \
--output tableTest network connectivity from your location:
```bash # Test if the port is reachable (requires netcat) nc -zv my-database.xxxxx.us-east-1.rds.amazonaws.com 5432
# Or use telnet telnet my-database.xxxxx.us-east-1.rds.amazonaws.com 5432 ```
If you have an EC2 instance in the same VPC, test from there:
aws ssm send-command \
--instance-ids i-1234567890abcdef0 \
--document-name AWS-RunShellScript \
--parameters 'commands=["nc -zv my-database.xxxxx.us-east-1.rds.amazonaws.com 5432"]' \
--output textCheck DNS resolution:
```bash nslookup my-database.xxxxx.us-east-1.rds.amazonaws.com
# Or dig my-database.xxxxx.us-east-1.rds.amazonaws.com ```
Test the actual database connection with credentials:
```bash # For PostgreSQL psql -h my-database.xxxxx.us-east-1.rds.amazonaws.com -U admin -d postgres -c "SELECT 1;"
# For MySQL mysql -h my-database.xxxxx.us-east-1.rds.amazonaws.com -u admin -p -e "SELECT 1;" ```
Check if you're hitting connection limits:
aws rds describe-db-instances \
--db-instance-identifier my-database \
--query 'DBInstances[*].[DBInstanceClass,StorageType,AllocatedStorage]'Check CloudWatch metrics for connections:
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name DatabaseConnections \
--dimensions DBInstanceIdentifier=my-database \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Average,Maximum \
--output tableCommon Causes and Solutions
Security Group Blocking Access
The most common cause. The security group attached to the RDS instance must allow inbound traffic on the database port from your client's IP or security group.
First, identify your client's IP or security group:
- For EC2 instances: Get the instance's security group
- For local development: Get your public IP
- For services: Get their IP range or security group
Add an inbound rule to the RDS security group:
```bash # Allow access from a specific IP (for local development) aws ec2 authorize-security-group-ingress \ --group-id sg-12345678 \ --protocol tcp \ --port 5432 \ --cidr 203.0.113.50/32
# Allow access from an EC2 security group (for applications) aws ec2 authorize-security-group-ingress \ --group-id sg-12345678 \ --protocol tcp \ --port 5432 \ --source-group sg-87654321 ```
For MySQL, use port 3306. For other engines, adjust accordingly: - PostgreSQL: 5432 - MySQL: 3306 - MariaDB: 3306 - SQL Server: 1433 - Oracle: 1521
Verify the rule was added:
aws ec2 describe-security-groups \
--group-ids sg-12345678 \
--query 'SecurityGroups[*].IpPermissions[?FromPort==`5432`]'Wrong VPC or Subnet
If your RDS instance is in a different VPC than your application, they can't communicate directly. Check which VPC the instance is in:
aws rds describe-db-instances \
--db-instance-identifier my-database \
--query 'DBInstances[*].DBSubnetGroup.VpcId'Compare with your application's VPC:
aws ec2 describe-instances \
--instance-ids i-1234567890abcdef0 \
--query 'Reservations[*].Instances[*].VpcId'If they're in different VPCs, you have several options:
- 1.VPC Peering - Connect the VPCs:
```bash # Create peering connection aws ec2 create-vpc-peering-connection \ --vpc-id vpc-app \ --peer-vpc-id vpc-db
# Accept the peering request (in the other VPC's account if cross-account) aws ec2 accept-vpc-peering-connection \ --vpc-peering-connection-id pcx-12345678
# Add route in the application VPC aws ec2 create-route \ --route-table-id rtb-app \ --destination-cidr-block 10.0.2.0/24 \ --vpc-peering-connection-id pcx-12345678
# Add route in the database VPC aws ec2 create-route \ --route-table-id rtb-db \ --destination-cidr-block 10.0.1.0/24 \ --vpc-peering-connection-id pcx-12345678 ```
- 1.Move the RDS instance - Create a new instance in the correct VPC and migrate data
Public Access Disabled
If you're connecting from outside AWS, the RDS instance must be publicly accessible. Check:
aws rds describe-db-instances \
--db-instance-identifier my-database \
--query 'DBInstances[*].PubliclyAccessible'If it returns false, you can enable public access:
aws rds modify-db-instance \
--db-instance-identifier my-database \
--publicly-accessible \
--apply-immediatelyHowever, this is not recommended for production. A better approach is to use a bastion host or VPN.
Create a bastion host for secure access:
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--key-name my-key-pair \
--security-group-ids sg-bastion \
--subnet-id subnet-publicThen tunnel to RDS through the bastion:
ssh -i my-key.pem -L 5432:my-database.xxxxx.us-east-1.rds.amazonaws.com:5432 ec2-user@bastion-public-ipNow connect to localhost:5432 instead.
DNS Resolution Issues
Sometimes the RDS endpoint doesn't resolve correctly. This can happen with: - Custom DNS settings - VPC DNS configuration issues - Split-horizon DNS problems
Check if your VPC has DNS resolution enabled:
aws ec2 describe-vpcs \
--vpc-ids vpc-12345678 \
--query 'Vpcs[*].[EnableDnsSupport,EnableDnsHostnames]'Both should be true. Enable them if needed:
```bash aws ec2 modify-vpc-attribute \ --vpc-id vpc-12345678 \ --enable-dns-support
aws ec2 modify-vpc-attribute \ --vpc-id vpc-12345678 \ --enable-dns-hostnames ```
Try connecting by IP instead of hostname (not recommended for production as RDS IPs can change):
```bash # Get the IP nslookup my-database.xxxxx.us-east-1.rds.amazonaws.com
# Connect directly psql -h 10.0.1.50 -U admin -d postgres ```
Connection Limit Exhausted
RDS has a maximum number of connections based on instance class. Check if you're hitting the limit:
# Check current connections via CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name DatabaseConnections \
--dimensions DBInstanceIdentifier=my-database \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics MaximumIf connections are maxed out, check for connection leaks in your application. You might also need to increase the instance class:
aws rds modify-db-instance \
--db-instance-identifier my-database \
--db-instance-class db.t3.large \
--apply-immediatelyOr implement connection pooling using: - PgBouncer for PostgreSQL - ProxySQL for MySQL - Amazon RDS Proxy
Create an RDS Proxy:
aws rds create-db-proxy \
--db-proxy-name my-proxy \
--engine-family POSTGRESQL \
--auth AuthScheme=SECRETS,SecretArn=arn:aws:secretsmanager:...,IAMAuth=REQUIRED \
--role-arn arn:aws:iam::123456789012:role/rds-proxy-role \
--vpc-subnet-ids subnet-1 subnet-2 subnet-3 \
--vpc-security-group-ids sg-12345678Database Overload
If the database is overloaded, it might not accept new connections. Check CPU and memory:
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name CPUUtilization \
--dimensions DBInstanceIdentifier=my-database \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Average,Maximum \
--output tableIf CPU is high, identify and optimize slow queries:
# Enable Performance Insights if not already enabled
aws rds modify-db-instance \
--db-instance-identifier my-database \
--enable-performance-insights \
--apply-immediatelyVerification Steps
After making changes, verify connectivity:
```bash # Test port connectivity nc -zv my-database.xxxxx.us-east-1.rds.amazonaws.com 5432
# Test database connection psql -h my-database.xxxxx.us-east-1.rds.amazonaws.com -U admin -d postgres -c "SELECT version();" ```
Create a simple test script to validate your application can connect:
# For a Python application
python3 -c "
import psycopg2
conn = psycopg2.connect(
host='my-database.xxxxx.us-east-1.rds.amazonaws.com',
database='postgres',
user='admin',
password='your-password'
)
print('Connection successful!')
conn.close()
"Set up monitoring for future issues:
aws cloudwatch put-metric-alarm \
--alarm-name rds-connections-high \
--alarm-description "RDS database connections approaching limit" \
--namespace AWS/RDS \
--metric-name DatabaseConnections \
--dimensions DBInstanceIdentifier=my-database \
--statistic Average \
--period 300 \
--threshold 80 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 2 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:my-alerts