RDS "Could Not Connect to Server" Timeout: Security Groups, Public Access and VPC, in Order

Logeshwaran.C

If you're staring at "could not connect to server: Connection timed out" or "ERROR 2003 (HY000): Can't connect to MySQL server" on an Amazon RDS instance, the fix is almost always one of three things, checked in this order: the instance isn't set to Publicly Accessible, its security group has no inbound rule for your IP and port, or a network ACL or route table is quietly blocking the path. Here's the part that trips people up: reopening the security group to the whole internet — the first thing everyone tries — fixes nothing if Publicly Accessible is still set to No. That setting removes an entire layer the security group can't touch on its own.

⚡ Quick Answer

Check Publicly Accessible first → Console > RDS > your instance > Connectivity & security

Then fix the security group → add an inbound rule for your IP on port 3306 (MySQL) or 5432 (PostgreSQL)

If both of those are already correct, the problem is almost certainly a network ACL, route table, or you're on the wrong VPC entirely. Jump to the security group steps or the NACL section below.

Why "Connection Timed Out" Almost Never Means Your Database Is Broken

Jake runs a small phone shop, and last Saturday his repair-ticket app — a little Node script on an EC2 box, talking to an Amazon RDS MySQL instance — froze mid-queue. A walk-in customer waited eleven minutes for a screen that never loaded, then left for the shop across the street. The app hadn't crashed. The database hadn't crashed. Something in between the two had quietly stopped letting them talk.

That's what a timeout actually tells you. It's different from an authentication error, which comes back fast with a clear "access denied" because the two sides did connect and then disagreed about credentials. A timeout means your client sent a request and nothing ever answered — not "no", just silence, until the client gives up. That silence points at the network path, not the database engine itself: a security group, a "Publicly Accessible" flag, a network ACL, a route table, or a DNS/endpoint mistake. Fixing the wrong one of those (usually the security group, because it's the most visible) while leaving the real blocker in place is the single most common reason people spend an afternoon on what should be a five-minute fix.

Step 1 — Confirm the Instance Is Actually Up

Before touching any network setting, rule out the boring cause: the instance itself isn't ready yet. New instances and instances coming out of a reboot, a storage scaling event, or a failover can take a while to report as available, and every connection attempt during that window will time out or refuse.

  1. Open the RDS console and check the Status column for your instance, or run aws rds describe-db-instances --db-instance-identifier YOUR-INSTANCE --query 'DBInstances[*].DBInstanceStatus' from a machine with the AWS CLI configured.
  2. If the status reads anything other than available — creating, backing-up, modifying, maintenance — wait. New or recently modified instances can take up to about 20 minutes to settle.
  3. If the status is failed or incompatible-network, open the Logs & events tab on the instance page; it lists the specific resolution steps for that state rather than a generic timeout.

Once the instance shows available and you're still timing out, the problem is downstream in the network path, and the rest of this post is written in the order that actually clears it fastest.

Step 2 — Is "Publicly Accessible" Actually Set to Yes?

🙋‍♂️ Jake's Reality Check

"I thought giving it a public IP address was the risky part. Why do I need that AND a security group rule? Isn't that redundant?"

It's not redundant — they're two separate locks, and both have to be open. One controls whether the database gets a public network address at all. The other controls who's allowed to talk to it once it has one.

Ethan puts it this way: "Publicly Accessible is the front door existing. Your security group is who's on the guest list. You can hand out every invitation in the world, but if the building has no door facing the street, nobody's getting in — and that's exactly what a private-only RDS instance is." When Publicly Accessible is set to No, your DB instance still gets an endpoint and a DNS name, but that name never resolves to an address reachable from outside the VPC. Every connection attempt from your laptop or from any host outside that VPC times out, no matter what the security group says.

To check and fix it: open the instance in the RDS console, look at the Connectivity & security tab for the Publicly accessible value. If it reads No and you need internet-based access, choose Modify, change Public access to Yes, and apply the change — this typically doesn't require downtime, but confirm the "Apply immediately" setting matches what you want before submitting it.

⚠️ What this actually breaks

The popular advice of "just open the security group to 0.0.0.0/0" is often tried right here — and if Publicly Accessible is still No, it accomplishes nothing except leaving a wide-open inbound rule sitting on the instance for no benefit. If you later do make it public, go back and narrow that rule; a database open to the entire internet on its default port is a standing invitation for credential-stuffing and brute-force login attempts.

Step 3 — Security Groups: Get the Right Rule on the Right Group

A VPC security group acts like a firewall attached directly to the instance's network interface. By default, a newly created security group allows no inbound traffic at all — everything has to be explicitly opened. An RDS instance can have more than one security group attached, and if any one of them allows your traffic, the connection is permitted; it's a "most permissive wins" model, not "all must agree."

  1. Find which security groups are attached: aws rds describe-db-instances --db-instance-identifier YOUR-INSTANCE --query 'DBInstances[*].VpcSecurityGroups[*].VpcSecurityGroupId'
  2. Check each one's inbound rules: aws ec2 describe-security-groups --group-ids YOUR-SG-ID --query 'SecurityGroups[*].IpPermissions'
  3. Add a rule for your source and port: aws ec2 authorize-security-group-ingress --group-id YOUR-SG-ID --protocol tcp --port 3306 --cidr YOUR-PUBLIC-IP/32 — swap 3306 for 5432 on PostgreSQL, or your custom port if you changed it.
Engine Default port Command to verify
MySQL / MariaDB 3306 mysql -h endpoint -P 3306 -u user -p
PostgreSQL 5432 psql -h endpoint -p 5432 -U user -d database
SQL Server 1433 Any SQL Server client, endpoint:1433

✅ Why this is the one to use

Scope the rule to your actual public IP as a /32, not to 0.0.0.0/0. Your home or office IP is usually static enough for day-to-day work, and a /32 rule gives you the exact same access with none of the exposure of leaving the port open to every address on the internet.

Remember that your own public IP isn't your computer's local address — it's the address the internet sees you as, and it changes if your ISP reassigns it or you switch networks (coffee shop Wi-Fi, a different office, a VPN). If a connection worked yesterday and times out today with nothing else changed, checking whether your public IP moved is the first thing to try before touching anything in the console.

Step 4 — Network ACLs (the Layer Everyone Forgets)

Security groups get all the attention because they live on the RDS console's Connectivity tab. Network ACLs (access control lists) sit one layer up, on the subnet itself, and RDS doesn't show them to you anywhere — you have to go find them in the VPC console. That's exactly why they're the thing people forget.

The practical difference: a security group is stateful (allow the inbound request, and the reply is automatically allowed back out), while a network ACL is stateless — you must explicitly allow both directions. That trips people up specifically on the return traffic: if you open inbound 3306 on the NACL but forget outbound, the request reaches the database and the reply never makes it back, which still looks like a timeout from the client's side.

To check it: aws ec2 describe-network-acls --filters "Name=association.subnet-id,Values=YOUR-SUBNET-ID" --query 'NetworkAcls[*].NetworkAclId', then aws ec2 describe-network-acls --network-acl-ids YOUR-NACL-ID --query 'NetworkAcls[*].Entries'. Confirm inbound rules allow your database port and that outbound rules allow the ephemeral port range, 1024–65535 — that range is where the client's side of the conversation happens, and a NACL that only opens 3306 outbound will silently break every connection.

Step 5 — VPC Subnets and Route Tables

An RDS instance lives inside a VPC, and specifically inside a DB subnet group — a set of subnets, usually spread across at least two Availability Zones, that RDS is allowed to place the instance in. Publicly Accessible and the security group control who's allowed in; the route table controls whether there's a path at all.

For a public subnet, the route table needs a route to an internet gateway. For a private subnet, there's no such route by design — instances there can typically reach the internet outbound through a NAT gateway (if one exists), but nothing on the internet can reach them directly, regardless of what the security group says. If your RDS instance sits in a subnet without a route to an internet gateway, setting Publicly Accessible to Yes still won't let you connect from outside the VPC; you'd need to either move it to a subnet that has that route, or connect through one of the private-access methods further down this post.

Check the route table for your RDS subnet with aws ec2 describe-route-tables --filters "Name=association.subnet-id,Values=YOUR-SUBNET-ID". If you expect public access and don't see a route to an internet gateway, that's your answer — no security group rule will fix it.

Step 6 — Wrong Endpoint, Wrong Port, or a DNS Problem

Sometimes the network path is completely fine and the connection string is simply wrong. Confirm the exact host name and port from the RDS console's Connectivity & security tab — not one copied from an old script, a teammate's notes, or a different environment. A cluster (Aurora-style) writer endpoint and an instance endpoint look similar and are not interchangeable.

Test DNS resolution directly: nslookup your-instance.xxxxx.rds.amazonaws.com or dig your-instance.xxxxx.rds.amazonaws.com. If that fails to resolve at all, the problem is upstream of security groups entirely — check your VPC's DNS resolution and DNS hostnames settings, or try resolving from a different network to isolate whether it's your local DNS server misbehaving.

Also check whether the port you're using is already occupied locally. If you have a MySQL or PostgreSQL server running on your own machine on the same default port, some clients will silently connect to that instead of failing loudly — worth ruling out if the "connection" behaves strangely rather than timing out cleanly.

Step 7 — Connecting From a Different VPC, Account, or On-Prem Network

If your application server is in a different VPC than the RDS instance — even in the same AWS account — the two networks don't talk to each other by default. You need explicit connectivity: a VPC peering connection between the two VPCs, or a Transit Gateway if there are several VPCs involved, plus route table entries on both sides and security group rules that reference the peer's CIDR range or security group ID. Peering also has a DNS side people miss: unless both VPCs have DNS resolution enabled for the peering connection, resolving the RDS endpoint's private DNS name from across the peering link can fail even after the routing itself is correct, which produces the same timeout as a routing miss.

The same logic applies to an on-premises network reaching RDS: you need a Site-to-Site VPN or Direct Connect link, and the route tables and NACLs on the RDS side have to know about that path too. One VPN-specific trap worth checking: if your local VPN client uses split tunneling — routing only some traffic through the tunnel and sending the rest out your regular internet connection — and the RDS instance's private IP range isn't included in the tunneled routes, your connection attempt goes out your normal internet path instead of through the VPN and simply times out, with nothing in AWS to blame. Skipping any one of these pieces reproduces the exact same "Connection timed out" symptom as a plain security group miss, which is why cross-VPC and on-prem setups are disproportionately represented in "I checked everything and it still doesn't work" reports.

Connecting From AWS Lambda or Another Serverless Client

Jake doesn't run anything serverless, but if you do, this one's worth its own section because it produces a completely different failure pattern. "Do I need to worry about any of this if I'm not using Lambda?" he asked. Ethan's answer: "No — skip straight past this one if your app runs on a regular server. This is a Lambda-specific gotcha."

A Lambda function attached to a VPC gets network interfaces in the subnets and security groups you assign it, and from there it can reach an RDS instance in the same VPC over a private IP with no internet gateway or NAT involved at all — that part isn't the problem. The problem shows up under load: every concurrent invocation can open its own database connection, and a traffic spike can spin up far more concurrent connections than a steady-state application ever would, exhausting max_connections in a way that looks like a wall of "too many connections" errors rather than a single timeout. Amazon RDS Proxy exists specifically for this pattern — it pools and reuses connections in front of the database, so a burst of Lambda invocations shares a much smaller, steady set of database connections instead of each one opening its own. You can attach a proxy to a Lambda function directly from the function's configuration tab in the console, and RDS Proxy supports IAM authentication for the Lambda-to-proxy leg, so the function's execution role can connect without a database password living anywhere in its code.

Method Needs RDS to be public? Best for
App server in the same VPC No Any production app tier
Bastion / jump host No Occasional admin access from a laptop
Site-to-Site VPN / Direct Connect No On-prem apps, corporate networks
Publicly Accessible + scoped security group Yes Individual developers connecting from home/office

Going Private Instead of Public

Jake's follow-up question was the right one: "Do I actually need Publicly Accessible turned on at all?" For a lot of setups, no — and Ethan's opinion here is blunt: "If your app server is in the same VPC as the database, making the database public is solving a problem you don't have and creating one you didn't want."

A bastion host is just a small EC2 instance in the same VPC as the RDS instance, reachable by SSH (or by AWS Systems Manager Session Manager, which avoids opening SSH to the internet at all), that you tunnel your database client through. Your RDS security group only ever needs to trust the bastion's security group — never your own changing home IP. That single change also solves the "worked yesterday, times out today" problem for good, since the bastion's IP inside the VPC doesn't move the way your home connection does.

When It's Not Actually a Timeout: Authentication and Connection-Limit Errors

If the network path is genuinely fine — you can reach the port with a plain TCP test — but the database itself refuses or drops the connection, you're dealing with a different family of problems entirely. For MySQL, a full instance returns "ERROR 1040: Too many connections" once max_connections is reached. For PostgreSQL, it's "FATAL: remaining connection slots are reserved for non-replication superuser connections," because a handful of slots are always held back for administrative use.

🕐 What changed between versions

  • Before: the reserved-connections behavior for RDS for PostgreSQL was governed by rds.rds_superuser_reserved_connections, alongside PostgreSQL's own superuser_reserved_connections.
  • Now: starting in RDS for PostgreSQL 17.1, 16.5, 15.9, 14.14, 13.17, and 12.21, a new rds_reserved role holds slots for Amazon's own administrative users, sized by the rds.rds_reserved_connections parameter; rds.rds_superuser_reserved_connections is deprecated on version 16 in favor of the standard reserved_connections parameter.
  • What that means for you: if you've raised max_connections and are still short on usable slots, check which of these parameters your engine version actually uses — the older name silently does nothing on the newer versions.

To see where you stand, connect successfully once (this part isn't blocked by anything above) and run SHOW STATUS LIKE 'Threads_connected'; and SHOW VARIABLES LIKE 'max_connections'; on MySQL, or SELECT count(*) FROM pg_stat_activity; and SHOW max_connections; on PostgreSQL. If you're near the ceiling, look for idle connections your application isn't closing before raising the limit — a parameter group change here requires a reboot to take effect for max_connections. For a workload with many short-lived connections, RDS Proxy pools them for you instead of raising limits indefinitely.

The Multi-AZ Failover Edge Case

One failure mode is easy to miss because nothing about it looks like a configuration mistake: a Multi-AZ instance fails over to its standby, and the standby lives in a different subnet — in a different Availability Zone — than the primary was in. That subnet is part of the same DB subnet group, but it's not guaranteed to have identical route table or NACL entries unless you set it up that way deliberately. Everything was correctly configured an hour ago; the underlying instance just isn't the one your rules were written for anymore.

If a previously working connection starts timing out with no changes on your end, check whether a failover event shows up in the instance's Recent events, and confirm the route tables and NACLs are consistent across every subnet in the DB subnet group — not just the one the primary happened to be using. This is also a good reason to double-check your subnet group spans at least two Availability Zones with matching network configuration before you need a failover to go smoothly, rather than discovering the mismatch during one.

Do Third-Party GUI Clients Change Any of This?

MySQL Workbench, DBeaver, TablePlus, pgAdmin — they all connect over the exact same network path as the command-line client. None of the steps above change depending on which one you use, and switching tools is never itself the fix for a timeout. Where a GUI client genuinely helps is bundling an SSH tunnel through a bastion host directly into its connection profile, so you're not running a separate ssh -L command by hand every time you connect.

Where they don't help: if connectivity is already broken, a nicer interface just gives you a nicer-looking timeout. Get the CLI client connecting first — it's faster to test with and rules out client-specific configuration as a variable — then move to a GUI tool once you know the path itself is open.

Before You Open Access: the Security Angle

Every fix on this page involves opening something up, so it's worth deciding what you're actually opening before you do it. A security group rule scoped to your personal /32 is far better than 0.0.0.0/0, but it's still a static, password-based front door. Where your engine supports it, IAM database authentication lets applications and users connect with short-lived, automatically rotating tokens tied to an IAM identity instead of a long-lived database password. For any credential you do keep, store it in AWS Secrets Manager rather than in application config, so it can be rotated without touching your code.

Least privilege applies inside the database too, not just at the network edge. A monitoring dashboard or a reporting job doesn't need the master user account — create a database role scoped to only the tables and permissions that job actually uses, so a leaked credential for one purpose doesn't hand over the whole instance.

Let AWS Diagnose It For You

Before working through every step above by hand, it's worth knowing AWS ships an automated runbook that checks most of this for you, if your application server is an EC2 instance.

  1. Run: aws ssm start-automation-execution --document-name "AWSSupport-TroubleshootConnectivityToRDS" --parameters "InstanceId=EC2-INSTANCE-ID,DBInstanceIdentifier=DB-INSTANCE-NAME"
  2. Check the result: aws ssm get-automation-execution --automation-execution-id EXECUTION-ID, using the execution ID returned by the first command.

It won't fix anything for you, but it narrows the search to the specific layer that's actually failing, which is usually the slow part of this whole process. For a multi-hop setup — VPC peering, a Transit Gateway, or a path that crosses several route tables — AWS's VPC Reachability Analyzer is worth reaching for next; you give it a source and a destination, and it traces the exact hop where the path breaks instead of you checking each hop by hand.

The Task Right After This One: Encrypting the Connection

Once you can connect, it's worth pausing on whether you should be connecting in the clear. Amazon RDS supports SSL/TLS connections for Db2, MariaDB, SQL Server, MySQL, Oracle, and PostgreSQL, which encrypts the traffic between your client and the instance. You can optionally go a step further and validate the server's identity by downloading the certificate bundle for your AWS Region and engine, then pointing your client at it during connection setup. A network path being open and a connection being encrypted are two different things — solving the timeout only gets you the first one, and it's a natural moment to handle the second before this instance goes back into daily use.

Quick Decision Table: Symptom to Likely Cause

Symptom Most likely cause Where to look
Times out from your laptop, works from an EC2 box in the VPCPublicly Accessible = No, or missing SG rule for your IPConnectivity & security tab
Worked yesterday, times out today, nothing changedYour public IP changedRecheck your current IP, update the SG rule
Inbound rule looks correct, still times outNetwork ACL missing outbound ephemeral portsVPC console > subnet > Network ACL
TCP connects fine, database rejects the loginAuthentication, not networkingCredentials, CONNECT grant, user host mask
Lambda works fine at low traffic, fails under a burstConcurrent invocations exhausting max_connectionsAdd RDS Proxy in front of the database
Fails only after a failover eventStandby subnet's route table/NACL differsCompare route tables across the DB subnet group

Frequently Asked Questions

How do I connect to RDS from my local machine?

Use the exact endpoint and port shown on the instance's Connectivity & security tab, with a standard client for your engine (mysql, psql, or a SQL Server client). That only succeeds if Publicly Accessible is Yes and your security group allows your current IP on that port.

How do I troubleshoot RDS connection issues fastest?

Check Publicly Accessible first, then the security group, then network ACLs, then route tables — in that order, because each one only matters if the layer before it is already correct.

What does "publicly accessible" actually control?

It controls whether the instance gets a network address reachable from outside its VPC at all. It's separate from the security group, which controls who's allowed to use that address once it exists.

Why does opening my security group to 0.0.0.0/0 still not work?

Almost always because Publicly Accessible is still set to No, so there's no public path for the security group's rule to even apply to. It's also a bad idea to leave a database open to the whole internet once you do fix that — scope the rule to your actual IP instead.

What's the default port for MySQL and PostgreSQL on RDS?

3306 for MySQL and MariaDB, 5432 for PostgreSQL, 1433 for SQL Server. You can change the port when you create or modify the instance, but the security group rule has to match whatever port you actually chose.

How do I find my current public IP to add to a security group?

In the RDS or EC2 console, the security group edit screen offers a "My IP" option that auto-fills your current public address. That's the address to use, not your computer's local network address.

Why did my connection work yesterday and time out today with nothing changed?

Your public IP most likely changed — home and small-office ISPs frequently reassign it, and switching networks (a coffee shop, a VPN, a new office) always changes it. Recheck your current IP and update the security group rule.

Can I connect to RDS without making it publicly accessible?

Yes, and for anything running inside AWS you generally should. An app server in the same VPC connects directly with no public exposure needed; a bastion host or Systems Manager Session Manager covers occasional admin access from outside.

What is rds.rds_reserved_connections and why does it matter?

On newer RDS for PostgreSQL versions, it sets how many connection slots are held back for Amazon's own administrative role. If your max_connections looks high enough but you're still hitting a reserved-slots error, this parameter (or its older equivalent, depending on your engine version) is worth checking.

Why do I get "Error reading from connection" instead of a timeout?

That usually means a connection was established and then dropped mid-session, which is a different problem from a timeout — check idle connection timeouts, network stability between the client and RDS, and whether the instance hit a connection or resource limit partway through.

Does Multi-AZ failover break my connection settings?

It can, if the standby's subnet has a route table or network ACL that isn't configured the same way as the primary's. The endpoint name doesn't change, but the underlying instance and its subnet do.

How do I check network ACLs versus security groups?

Security groups are stateful and attach to the instance; network ACLs are stateless and attach to the subnet. Check both — a NACL missing outbound ephemeral ports (1024–65535) can block a connection even when the security group is perfectly correct.

My EC2 instance is in the same VPC — why can't it connect?

Confirm the EC2 instance's own security group is allowed as a source in the RDS security group's inbound rule (or that its IP/CIDR is), and that both instances are actually in the same VPC and not just the same AWS account or region.

How long does an RDS instance take to become available after a change?

Often just a few minutes, but new instances or larger modifications can take up to about 20 minutes. Check the Status column before assuming a timeout is a network problem.

Should I use RDS Proxy or a bastion host?

They solve different problems. RDS Proxy pools and manages database connections for an application with many short-lived connections, such as Lambda; a bastion host is for occasional human access from outside the VPC. Many production setups use both, for different purposes.

Is it safe to leave my RDS instance publicly accessible?

It's a real exposure if the security group isn't tightly scoped, since a public, default-port database is a known target for automated login attempts. If nothing outside the VPC genuinely needs direct access, turning Publicly Accessible off and using a bastion or same-VPC access instead removes that exposure entirely.

Revision note. Written September 2026, covering current RDS console behavior for MySQL, PostgreSQL, and SQL Server DB instances, including the reserved-connections changes on recent RDS for PostgreSQL versions and RDS Proxy's role in Lambda connection storms. This will need a look whenever AWS reshuffles the Connectivity & security tab or changes how reserved connection slots are named. If you've been going in circles on this one, you're not missing something obvious — the layers genuinely don't show each other's state, and that's the whole reason this took longer than it should have.

Related