AWS Builder Center

Private Doesn't Mean Inaccessible: Connect to Amazon RDS Using AWS Systems Manager Port Forwarding

Learn how to securely connect to a private Amazon RDS database from your local machine using an Amazon EC2 managed node and AWS Systems Manager Session Manager—without making the database publicly accessible, opening SSH ports, or maintaining a bastion host.

Private Doesn't Mean Inaccessible: Connect to Amazon RDS Using AWS Systems Manager Port Forwarding

Amazon RDS is commonly placed in a private subnet so that the database cannot be reached directly from the public internet. This is a good security practice, but it creates a practical question:
How do you connect to a private RDS database from your local machine for development, debugging, or administration?
A common solution is to deploy a bastion host and connect to it over SSH. However, AWS Systems Manager Session Manager provides another approach that removes the need to expose SSH ports or maintain a dedicated bastion host.
In this article, we will build a secure connection path:Why Put RDS in a Private Subnet?
A database usually should not be directly exposed to the internet.
For example, a typical architecture might look like:
1
2
3
4
5
6
7
8
9
10
11
12
Internet
|
v
Application / API
|
Private Network
|
v
+-------------+
| RDS |
| Database |
+-------------+
The database security group can allow database traffic only from trusted resources such as an application server.
For MySQL, the default port is:
1
3306
For PostgreSQL:
1
5432
Instead of allowing traffic from the entire internet, the RDS security group should allow the database port from the security group attached to the EC2/application server.
This gives us an important security property:
1
2
3
4
Internet
X
|
X----> RDS
The RDS instance does not need to accept public inbound connections.

The Traditional Approach: Bastion Host

One way to access a private database is:
1
2
3
4
5
6
7
8
9
Laptop
|
| SSH
v
Bastion Host
|
| Database connection
v
RDS
Although this works, it introduces additional operational requirements:
  • The bastion host must be maintained.
  • SSH access must be controlled.
  • SSH keys need to be managed.
  • Port 22 may need to be exposed.
  • The bastion becomes another infrastructure component to secure.
AWS Systems Manager Session Manager provides a way to manage EC2 instances without opening inbound ports or maintaining SSH access.

The Better Alternative: Session Manager Port Forwarding

AWS Systems Manager Session Manager can create port forwarding sessions from your local machine to a managed node.
There is also a useful feature called:
1
AWS-StartPortForwardingSessionToRemoteHost
This allows an EC2 instance managed by Systems Manager to act as the network bridge to another host.
The remote host does not need to be managed by Systems Manager.
That makes it a good fit for a private RDS endpoint.
Our final connection will look like this:
1
2
3
4
5
6
7
8
9
10
11
12
Local MySQL Client
|
| localhost:3307
v
Session Manager
|
v
EC2 Instance
|
| RDS:3306
v
Private RDS

Prerequisites

Before starting, you need:
  1. An AWS account
  2. A VPC containing:
    • An EC2 instance
    • An RDS instance
  3. AWS CLI installed locally
  4. Session Manager plugin installed locally
  5. An EC2 instance registered as a Systems Manager managed node
  6. Appropriate IAM permissions
For the EC2 instance, attach an IAM role containing:
1
AmazonSSMManagedInstanceCore
The EC2 instance also needs network connectivity to the Systems Manager endpoints.
An EC2 instance can use outbound internet access through a NAT Gateway, or you can configure the required Systems Manager VPC endpoints when keeping the instance fully private.

Step 1: Create a Private RDS Instance

Create your RDS database inside your VPC.
For example:
1
2
3
Engine: MySQL
Port: 3306
Public access: No
Make sure the RDS instance has a DB subnet group that uses private subnets.
After creation, you will receive an RDS endpoint similar to:
1
mydatabase.xxxxxxxxx.ap-south-1.rds.amazonaws.com
Do not use this endpoint as if it were publicly reachable.
It is simply the hostname that the EC2 instance will use inside the VPC.

Step 2: Configure the RDS Security Group

Create a security group for the database.
For MySQL, configure:
1
2
3
4
5
6
Inbound Rule

Type: MySQL/Aurora
Protocol: TCP
Port: 3306
Source: EC2 security group
The important part is the source.
Avoid:
1
0.0.0.0/0
Instead, reference the security group attached to the EC2 instance.
The resulting relationship is:
1
2
3
4
5
EC2 Security Group
|
| TCP 3306
v
RDS Security Group
This means the database accepts traffic from the EC2 instance, not from arbitrary internet clients.

Step 3: Prepare the EC2 Instance

Launch an EC2 instance inside the same VPC.
The EC2 instance does not need to expose SSH to the internet for this setup.
Make sure:
1
2
3
4
5
6
7
EC2
|
+-- Systems Manager managed
|
+-- Can resolve RDS endpoint
|
+-- Can reach RDS on TCP 3306
Check that the IAM role attached to the instance includes:
1
AmazonSSMManagedInstanceCore
You can verify the instance appears under:
1
2
3
AWS Systems Manager
->
Fleet Manager / Managed Nodes
The instance should show as available for Session Manager connections.

Step 4: Verify EC2-to-RDS Connectivity

This step is extremely important.
Port forwarding does not magically bypass VPC networking.
The EC2 instance itself must be able to reach the RDS endpoint.
For example:
1
EC2 ----TCP 3306----> RDS
If this connection fails, Session Manager port forwarding will also fail.
Common things to verify are:
  • Security group rules
  • Route tables
  • Network ACLs
  • DNS resolution
  • Correct RDS port
  • Correct RDS endpoint

Step 5: Install the Session Manager Plugin

Your local computer needs the AWS Session Manager plugin to establish CLI sessions.
After installation, verify the AWS CLI and plugin are available.
For example:
1
aws --version
Then verify that your AWS credentials are configured:
1
aws sts get-caller-identity
This should return information about the AWS identity being used by your CLI.

Step 6: Start Port Forwarding

Now we can create the tunnel.
Suppose:
1
2
3
4
5
6
7
8
9
10
11
EC2 instance:
i-0123456789abcdef0

RDS endpoint:
mydatabase.xxxxxxxxx.ap-south-1.rds.amazonaws.com

RDS port:
3306

Local port:
3307
Run:
1
2
3
4
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["mydatabase.xxxxxxxxx.ap-south-1.rds.amazonaws.com"],"portNumber":["3306"],"localPortNumber":["3307"]}'
On Windows, you can use:
1
aws ssm start-session --target i-0123456789abcdef0 --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters host="mydatabase.xxxxxxxxx.ap-south-1.rds.amazonaws.com",portNumber="3306",localPortNumber="3307"
A successful session will indicate that the local port has been opened.
At this point:
1
localhost:3307
on your computer is connected through the Session Manager tunnel to:
1
RDS:3306

Step 7: Connect Using Your Database Client

Now comes the interesting part.
Your database client does not need to know that RDS is inside a private subnet.
Instead of connecting to:
1
mydatabase.xxxxxxxxx.ap-south-1.rds.amazonaws.com:3306
connect to:
1
127.0.0.1:3307
For example, with MySQL:
1
mysql -h 127.0.0.1 -P 3307 -u admin -p
Your traffic follows this path:
1
2
3
4
5
6
7
8
9
10
11
12
MySQL Client
|
| localhost:3307
v
Session Manager Tunnel
|
v
EC2
|
| mydatabase...:3306
v
Amazon RDS
Your database is still private.

Why This Is More Secure

Notice what we did NOT do.
We did not:
1
Open RDS to 0.0.0.0/0
We did not:
1
Expose RDS publicly
We did not need:
1
SSH port 22
We did not need to maintain:
1
A dedicated bastion host
Instead, access is controlled through AWS IAM and Systems Manager.
This is an important architectural principle:
Keep the database private and move the access mechanism closer to the identity layer.
The network path is still enforced by VPC networking and security groups, while the session itself is initiated through AWS Systems Manager.

Understanding the Security Groups

Let's simplify the configuration.

EC2 Security Group

The EC2 instance needs outbound connectivity to the RDS instance.
For example:
1
2
3
4
EC2 SG
Outbound
|
+---- TCP 3306 ----> RDS SG

RDS Security Group

The RDS security group allows:
1
2
Inbound TCP 3306
Source = EC2 Security Group
This is much better than:
1
2
Inbound TCP 3306
Source = 0.0.0.0/0
because the database trusts a particular application/server security group rather than the entire internet.

What Happens Internally?

The command uses the Systems Manager document:
1
AWS-StartPortForwardingSessionToRemoteHost
The EC2 instance is the managed node.
Systems Manager creates the session between your local machine and the managed node.
The traffic is then forwarded from the EC2 instance to the specified remote host and port.
In our example:
1
2
3
4
5
6
7
8
Local port: 3307
|
v
EC2 managed node
|
v
Remote host: RDS endpoint
Remote port: 3306
The RDS server itself does not need to run the Systems Manager agent.
Only the EC2 managed node needs to be managed by Systems Manager.

Troubleshooting

1. "Target is not connected"

Check whether your EC2 instance appears as a managed node in Systems Manager.
Also verify:
1
AmazonSSMManagedInstanceCore
is attached to its IAM role.

2. Port forwarding starts but database connection fails

Check the EC2-to-RDS path.
Verify:
1
EC2 SG -> RDS SG
allows the database port.
For MySQL:
1
TCP 3306
For PostgreSQL:
1
TCP 5432

3. RDS endpoint cannot be resolved

Check DNS settings in the VPC and make sure the EC2 instance can resolve the RDS hostname.

4. Session Manager itself cannot connect

The EC2 instance needs connectivity to the Systems Manager services.
Depending on your architecture, this can be provided using:
1
NAT Gateway
or appropriate:
1
VPC endpoints
for Systems Manager connectivity.

5. Local port is already in use

If port 3307 is occupied, choose another local port.
For example:
1
2
Local port: 13306
Remote port: 3306
Then connect to:
1
127.0.0.1:13306

Production Considerations

Port forwarding is particularly useful for development, troubleshooting, migrations, and controlled administrative access.
For production environments, access should be carefully restricted.
Consider:
1
2
3
4
5
6
7
8
9
10
11
12
13
IAM least privilege
+
Private RDS
+
Restricted security groups
+
Systems Manager
+
Session logging where applicable
+
Encryption
+
Database authentication controls
You should also avoid sharing long-lived database credentials unnecessarily and should use appropriate AWS credential-management mechanisms for production workloads.

Bastion Host vs Session Manager

FeatureBastion HostSession Manager
SSH port requiredUsuallyNo
SSH keysUsuallyNo
Dedicated serverYesExisting managed node can be used
IAM-based accessOptionalYes
Private RDS accessYesYes
Operational overheadHigherLower
Port forwardingYesYes
Neither approach is universally correct, but Session Manager can significantly reduce the infrastructure and exposed attack surface required for administrative access.

Final Architecture

The final architecture looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Your Laptop
|
|
AWS CLI + SSM
|
v
AWS Systems Manager
|
|
Managed EC2 Instance
|
TCP 3306
|
v
+-------------------+
| Amazon RDS |
| Private Subnet |
+-------------------+
The key takeaway is simple:
A private database does not have to be a difficult database to access.
By combining Amazon RDS private networking, security groups, IAM, and AWS Systems Manager Session Manager, you can create a secure access path without exposing your database directly to the public internet.
This pattern is especially useful when you want the convenience of local database tools while keeping the database infrastructure private.

Conclusion

Private RDS deployments are an important part of secure AWS architectures, but developers and administrators still need controlled ways to access them.
AWS Systems Manager port forwarding provides a practical solution:
1
2
3
4
5
6
7
Private RDS
+
Managed EC2
+
Session Manager
=
Secure controlled database access
1
2
3
4
5
6
7
8
9
10
11
Local Machine
|
| Secure Session Manager tunnel
v
Amazon EC2
(SSM Managed Node)
|
| TCP 3306
v
Amazon RDS
(Private Subnet)
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article