Connecting to AWS RDS MySQL from Your Local Machine: Public Access, Security Groups, and Safer Alternatives

You've just provisioned an RDS MySQL instance on AWS, opened MySQL Workbench, and hit a wall — connection refused, timeout, nothing. This is one of the most common friction points for developers new to AWS networking, and the fix requires understanding three independent gatekeepers that must all be aligned before a single TCP packet reaches your database.

TL;DR — The Three Gatekeepers

GatekeeperWhat It ControlsWhat You Must Configure
1. Publicly Accessible FlagWhether RDS gets a public DNS hostnameSet to Yes for direct internet access
2. VPC Security GroupInbound/outbound firewall rules at the instance levelAllow TCP port 3306 from your IP
3. VPC / Subnet RoutingWhether the subnet has a route to the Internet GatewayRDS must be in a public subnet (has IGW route)

All three must be correctly configured simultaneously. Fixing only one or two will still result in a timeout.

Architecture: What's Actually Happening

graph LR Laptop["💻 Your Laptop
(MySQL Workbench)"] IGW["🌐 Internet Gateway
(VPC IGW)"] RT["📋 Route Table
0.0.0.0/0 → IGW"] SG["🛡️ Security Group
Inbound: TCP 3306"] RDS["🗄️ RDS MySQL
(Publicly Accessible: Yes)"] PublicFlag["🏷️ Public DNS
Hostname Assigned"] Laptop -->|"TCP :3306 over internet"| IGW IGW --> RT RT -->|"Routes to public subnet"| SG SG -->|"Rule matches: ALLOW"| RDS PublicFlag -.->|"Required for DNS resolution"| RDS style Laptop fill:#4A90D9,color:#fff style IGW fill:#F5A623,color:#fff style RT fill:#7ED321,color:#fff style SG fill:#D0021B,color:#fff style RDS fill:#9B59B6,color:#fff style PublicFlag fill:#1ABC9C,color:#fff
  1. Your Laptop initiates a TCP connection on port 3306 to the RDS public DNS endpoint.
  2. The request travels over the public internet to the AWS Internet Gateway (IGW) attached to your VPC.
  3. The IGW routes the packet to the RDS instance — but only if the instance resides in a public subnet (a subnet whose route table has a 0.0.0.0/0 route pointing to the IGW).
  4. The VPC Security Group evaluates the inbound rule. If TCP/3306 is not explicitly allowed from your source IP, the packet is silently dropped.
  5. The Publicly Accessible flag determines whether RDS even registers a public IP/DNS. Without it, step 2 has no destination to resolve to.

Step-by-Step: Enable Direct Public Access (Quick Path)

Use this approach for development/testing environments only. Never expose a production database directly to the internet.

Step 1 — Enable "Publicly Accessible" on the RDS Instance

  1. Open the RDS Console → select your DB instance → click Modify.
  2. Under Connectivity, set Publicly accessible to Yes.
  3. Choose Apply immediately and confirm. The instance will briefly restart.
  4. After modification, copy the Endpoint from the instance's Connectivity & security tab — it will be a public DNS name like mydb.xxxxxxxx.us-east-1.rds.amazonaws.com.

Step 2 — Verify the Subnet is Public

A subnet is "public" only if its associated route table has an explicit route to an Internet Gateway.

  1. Go to VPC ConsoleSubnets → find the subnet(s) associated with your RDS instance's subnet group.
  2. Click the subnet → Route table tab → confirm a route exists: Destination: 0.0.0.0/0, Target: igw-xxxxxxxx.
  3. If no such route exists, either move the RDS instance to a public subnet group, or add the IGW route (requires an IGW to be attached to the VPC).

Step 3 — Update the Security Group Inbound Rule

This is the most common misconfiguration. The default RDS security group has no inbound rules.

🔽 [Click to expand] — AWS CLI: Add inbound rule for your IP
# 1. Find your current public IP
curl -s https://checkip.amazonaws.com
# Output example: 203.0.113.45

# 2. Get the Security Group ID attached to your RDS instance
# (visible in RDS Console → Connectivity & security → VPC security groups)

# 3. Add the inbound rule (replace values accordingly)
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 3306 \
  --cidr 203.0.113.45/32 \
  --region us-east-1

Alternatively, via the Console: EC2 ConsoleSecurity Groups → select the group → Inbound rulesEdit inbound rules → Add rule: Type MySQL/Aurora, Source My IP.

⚠️ Never use 0.0.0.0/0 as the source for port 3306. Always restrict to your specific IP (/32 CIDR).

Step 4 — Connect from MySQL Workbench

Workbench FieldValue
Connection MethodStandard (TCP/IP)
Hostnamemydb.xxxxxxxx.us-east-1.rds.amazonaws.com
Port3306
UsernameYour RDS master username
PasswordYour RDS master password

The Production-Safe Alternative: SSH Tunnel via a Bastion Host

Analogy: Think of a Bastion Host as a secure reception desk in a locked building. You authenticate at the front desk (SSH), and the receptionist escorts your request to the private office (RDS) — the office itself has no public-facing door.

For any environment beyond local dev, keep RDS in a private subnet (no IGW route) and access it through an SSH tunnel via a Bastion EC2 instance or AWS Systems Manager Session Manager.

Option A: SSH Tunnel via Bastion EC2

graph LR Laptop["💻 Your Laptop
local port 3307"] Bastion["🖥️ Bastion EC2
Public Subnet
SSH port 22 open"] RDS["🗄️ RDS MySQL
Private Subnet
No public access"] SG_B["🛡️ Bastion SG
Allow SSH/22
from your IP"] SG_R["🛡️ RDS SG
Allow TCP/3306
from Bastion SG"] Laptop -->|"SSH Tunnel
-L 3307:rds-endpoint:3306"| Bastion Bastion -->|"Forwards to RDS
port 3306"| RDS SG_B -.->|"Guards"| Bastion SG_R -.->|"Guards"| RDS style Laptop fill:#4A90D9,color:#fff style Bastion fill:#F5A623,color:#fff style RDS fill:#9B59B6,color:#fff style SG_B fill:#D0021B,color:#fff style SG_R fill:#D0021B,color:#fff
🔽 [Click to expand] — SSH Tunnel Command
# This command forwards your local port 3307 to the RDS endpoint via the Bastion host.
# Replace all placeholder values.

ssh -N -L 3307:mydb.xxxxxxxx.us-east-1.rds.amazonaws.com:3306 \
    -i /path/to/your-key.pem \
    ec2-user@<BASTION_PUBLIC_IP>

# Then connect MySQL Workbench to:
# Hostname: 127.0.0.1
# Port:     3307

The Bastion EC2 instance's Security Group must allow inbound SSH (port 22) from your IP. The RDS Security Group must allow inbound TCP/3306 from the Bastion's Security Group ID (not your laptop IP).

Option B: AWS Systems Manager (SSM) Port Forwarding — No Bastion Required

This is the modern, keyless approach. It requires the SSM Agent on an EC2 instance in the same VPC, and no inbound Security Group rules are needed on that instance.

🔽 [Click to expand] — SSM Port Forwarding Command
# Prerequisites:
# 1. An EC2 instance in the same VPC with SSM Agent installed and an IAM role
#    that includes the AmazonSSMManagedInstanceCore managed policy.
# 2. AWS CLI with Session Manager plugin installed locally.

aws ssm start-session \
  --target i-0123456789abcdef0 \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"host":["mydb.xxxxxxxx.us-east-1.rds.amazonaws.com"],"portNumber":["3306"],"localPortNumber":["3307"]}' \
  --region us-east-1

# Then connect MySQL Workbench to:
# Hostname: 127.0.0.1
# Port:     3307

Decision Guide: Which Approach Should You Use?

graph TD Start(["🚀 Need to connect to RDS
from local machine"]) Q1{"Is this a
production environment?"} Q2{"Do you have an
EC2 instance in the VPC?"} A1["✅ Option: Direct Public Access
Enable Publicly Accessible
Restrict SG to your IP /32"] A2["✅ Option: SSM Port Forwarding
No open ports needed
Requires SSM Agent on EC2"] A3["✅ Option: SSH Bastion Tunnel
EC2 in public subnet
RDS stays private"] A4["⚠️ Launch an EC2 instance
with SSM role, then use
SSM Port Forwarding"] Start --> Q1 Q1 -->|"No - Dev/Test"| A1 Q1 -->|"Yes - Production"| Q2 Q2 -->|"Yes"| A2 Q2 -->|"Yes, with SSH key"| A3 Q2 -->|"No"| A4 style Start fill:#4A90D9,color:#fff style A1 fill:#F5A623,color:#fff style A2 fill:#27AE60,color:#fff style A3 fill:#27AE60,color:#fff style A4 fill:#E74C3C,color:#fff

Common Errors & Root Causes

Error / SymptomMost Likely Root CauseFix
Connection timed outSecurity Group missing inbound rule, or subnet has no IGW routeAdd SG inbound rule; verify subnet route table
Could not resolve hostname"Publicly Accessible" is set to No — no public DNS is assignedEnable Publicly Accessible and apply
Access denied for userWrong username/password, or DB user lacks remote login privilegesVerify credentials; check MySQL user host binding
SSL connection errorMySQL Workbench enforcing SSL but certificate not configuredDownload RDS CA bundle or disable SSL requirement for dev

IAM & Security Checklist

  • ✅ Restrict Security Group inbound rule to <your-ip>/32, never 0.0.0.0/0 for port 3306.
  • ✅ For production, keep RDS in a private subnet — use SSM or Bastion tunnel.
  • ✅ Enable RDS encryption at rest (KMS) and in-transit SSL/TLS.
  • ✅ Use IAM database authentication instead of static passwords where possible.
  • ✅ Rotate the RDS master password using AWS Secrets Manager.
  • ✅ Enable VPC Flow Logs to audit connection attempts to your RDS subnet.

Glossary

TermDefinition
Publicly AccessibleAn RDS instance attribute that, when enabled, causes AWS to assign a public DNS hostname and IP to the instance, making it reachable from outside the VPC.
Security GroupA stateful virtual firewall operating at the resource level within a VPC, controlling inbound and outbound traffic via allow-only rules.
Internet Gateway (IGW)A horizontally scaled, redundant VPC component that enables communication between resources in a VPC and the internet.
Bastion HostA hardened EC2 instance in a public subnet used as a secure jump server to access resources in private subnets.
SSM Session ManagerAn AWS Systems Manager capability that provides secure, auditable shell and port-forwarding access to EC2 instances without requiring open inbound ports or SSH keys.

Next Steps

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables

IAM User vs. IAM Role: Why Your EC2 Instance Should Never Use a User

EC2 SSH Connection Timeout: The Exact Security Group Rules You Need to Fix It